下面这4个案例来自大神“你假笨”(任职阿里期间,花名:寒泉子)在qcon上的分享,记录一下:

一、类加载死锁

现象:jstack将线程dump出来后,找不到deadlock字样的死锁信息,但是有大量的线程在调用Class.forName加载类

    @CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
} private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;

可以看到forName0是一个native方法,分析该方法的C++源码实现,可以发现使用了锁(细节略)

tips: jstack -m pid (可以看到native的详细输出信息,但不推荐生产上用,极端情况会让应用不稳定)

类加载在底层要加锁的原因也不难理解 ,如上图,如果三个线程并发加载类C,如果没有锁,最后可能会把类的元数据信息,在perm区(JDK8以前的版本,JDK8后取消了Perm区)中存多份,很容易造成内存泄露,所以需要加锁,加锁后变成下面这样:

这个并发加载的情况,从JDK7开始就做了优化,支持并发类加载,但是要使用该功能,必须注册成并行类加载器,否则仍然存在死锁可能。

参考文章:

https://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html

https://www.cnblogs.com/cz123/p/6918708.html

https://www.jianshu.com/p/8e8a5a773648

解决方法:

既然多线程并发加载可能出问题,那么就放在单线程里加载,可参考下面的示例,假设有2个类:Parent及Child

package com.cnblogs.yjmyzz.test;

public class Parent {
static {
System.out.println("Parent init.");
}
public static final Parent EMPTY = new Child();
public static void test() {
System.out.println("test called in class Parent.");
}
}

package com.cnblogs.yjmyzz.test;

public class Child extends Parent {
static {
System.out.println("Child init.");
}
}

如果用2个线程并发加载:

    public static void main(String[] args) {
new Thread(() -> new Child(), "T-1").start();
new Thread(() -> Parent.test(), "T-2").start();
}

T-1线程中,new Child()时,要先初始化父类Parent,需要加载类Parent,而T-2线程中调用Parent时,其static成员EMPTY又会尝试加载子类Child. 上述这段代码,如果试着运行几次,就有很大概率会遇到死锁:

会一直卡在这里。可以显式在主线程最开始用forName加载这2个类,这样类加载就变成在main线程中串行加载,问题得到解决:

    public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.cnblogs.yjmyzz.test.Parent");
Class.forName("com.cnblogs.yjmyzz.test.Child");
new Thread(() -> new Child(), "T-1").start();
new Thread(() -> Parent.test(), "T-2").start();
}

  

二、FinalReference堆积

现象:用jmap命令分析查看占用内存最多的对象时, 发现java.lang.ref.Finalizer实例排在最前面。

原因:

Object类有一个finalize方法,类似析构器,开发人员可以重载这个方法,用于清理资源。大多数情况下,java并不推荐重载该方法,因为jvm的GC已经把垃圾回收做得很好了。

但如果有某种原因,开发人员确实需要重载该方法:

    @Override
protected void finalize() throws Throwable {
//开发人员自定义的清理逻辑
}

即:这里有些自定义的清理逻辑。这种重载了finalize方法,且实现代码非空的类,在类加载时会被特殊标识,当实例创建时,被包装成FinalReference,放入一个队列里,当GC发生时,如果该实例被标识为垃圾对象,GC清理完后,会用一个额外的线程(重点:这是1个独立的单线程),从队列里一个个取出来,调用重载的finalize方法,如果这种对象在JVM中有大量实例,而且finalize里的清理逻辑,耗时又比较久的话,单线程忙不过来,只能等到下1个GC周期,才会继续清理,因此造成堆积。

建议:不用使用重载finalize的方式来清理资源。

三、堆外内存不释放

先回顾下堆外内存的分布,对于DirectByteBuffer之类的对象,JVM堆上只存放了其"引用",如下图,引用指向的实际内存块在JVM堆外(即:实际分配的堆外内存不受GC管控)

GC能管理的只是堆上的"引用"数据,但是这块数据通常又非常小,就算经过GC不停折腾,从年青代晋升到老年代,只要老年代的空间还够,就会一直存活,因此其指向的堆外内存也不会释放。除非发生Full GC,把"引用"数据给干掉了,其指向的堆外内存,才会被释放。

建议:使用-XX:MaxDirectMemorySize参数,限制堆外内存大小。

四、YGC时间不断拉长

现象:随着系统持续运行,单次YGC的时间越来越长。

可能原因:大量调用了String.intern方法,导致字符串的常量池越来越大,而每次YGC都要先mark标记,字符串常量池越大,需要扫描mark的对象也越多,时间就变长了。

排查方法:jmap -histo:live pid 强制触发一次Full GC,这会强制清理字符串常量池StringTable中无效的对象,如果YGC时间恢复,说明大概率就是这个原因。

JVM问题典型案例定位学习的更多相关文章

  1. NB-IoT的相关资料整理(基本概念,技术优势,典型案例和当前的进展)

            人与人之间的通讯规模已近天花板,物与物的则刚刚进入增长快车道.随着可穿戴.车联网.智能抄表等新兴市场的开启,工业4.0.智慧城市.智慧农业等理念照进现实,万物互联的时代正加速到来. 一 ...

  2. 《SAS编程和数据挖掘商业案例》学习笔记# 19

    继续<SAS编程与数据挖掘商业案例>学习笔记,本文側重数据处理实践.包含:HASH对象.自己定义format.以及功能强大的正則表達式 一:HASH对象 Hash对象又称散列表,是依据关键 ...

  3. 【Java新特性】Lambda表达式典型案例,你想要的的都在这儿了!!

    写在前面 不得不说,有些小伙伴的学习热情真高,学完了Lambda表达式的语法,想来几个典型案例再强化下.于是问冰河能否给几个Lambda表达式的典型使用示例.于是乎,便有了这篇文章. 案例一 需求 调 ...

  4. elasticsearch中的mapping映射配置与查询典型案例

    elasticsearch中的mapping映射配置与查询典型案例 elasticsearch中的mapping映射配置示例比如要搭建个中文新闻信息的搜索引擎,新闻有"标题".&q ...

  5. HBase基本知识介绍及典型案例分析

    本次分享的内容主要分为以下五点: HBase基本知识: HBase读写流程: RowKey设计要点: HBase生态介绍: HBase典型案例分析. 首先我们简单介绍一下 HBase 是什么. HBa ...

  6. 《SAS编程与数据挖掘商业案例》学习笔记之十六

    <SAS编程与数据挖掘商业案例>学习笔记,本次重点:sas宏变量 内容包含:宏变量.宏函数.宏參数.通配函数.字符函数.计算函数.引用函数.宏语句.宏应用 1.宏触发器: %name-to ...

  7. Agora 教程丨一个典型案例,教你如何使用水晶球“数据洞察”

    7 月初,声网Agora 水晶球的"数据洞察"功能正式版上线."数据洞察"可显示两种数据,一种是用量,另一种是质量. "数据洞察"的&quo ...

  8. 《深入理解Java虚拟机》-----第5章 jvm调优案例分析与实战

    案例分析 高性能硬件上的程序部署策略 例 如 ,一个15万PV/天左右的在线文档类型网站最近更换了硬件系统,新的硬件为4个CPU.16GB物理内存,操作系统为64位CentOS 5.4 , Resin ...

  9. Java之JVM调优案例分析与实战(1) - 高性能硬件上的程序部署策略

    本JVM系列均来源于<深入理解Java虚拟机>一书中,版权归该书作者所有. 环境:一个15万PV/天左右的在线文档类型网站最近更换了硬件系统,新系统硬件为4个CPU.16GB物理内存.OS ...

  10. 一个典型案例为你解读TDSQL 全时态数据库系统

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯技术工程官方号发表在腾讯云+社区 经典案例 增量抽取.增量计算等都是T-TDSQL的经典案例.如下以增量计算为例,来分析T-TDS ...

随机推荐

  1. 某公交管理系统简易逻辑漏洞+SQL注入挖掘

    某公交管理系统挖掘 SQL注入漏洞 前台通过给的账号密码,进去 按顺序依次点击1.2.3走一遍功能点,然后开启抓包点击4 当点击上图的4步骤按钮时,会抓到图下数据包,将其转发到burp的重放模块 构造 ...

  2. RPC实战与核心原理之时钟轮

    时钟轮在RPC中的应用 回顾 在分布式环境下,RPC 框架自身以及服务提供方的业务逻辑实现,都应该对异常进行合理地封装,让使用方可以根据异常快速地定位问题:而在依赖关系复杂且涉及多个部门合作的分布式系 ...

  3. ubuntu2004 ROS1安装

    ubuntu初始环境配置ROS1 1.换源并更新数据库 ubuntu2004换源 # 备份原来的源并且另存 sudo cp -v /etc/apt/sources.list /etc/apt/sour ...

  4. Client-go的四种客户端的简单使用

    Client-go的四种客户端使用 我们知道kubectl是通过命令行交互的方式与Kubernetes API Server进行交互的,Kubernetes还提供了通过编程的方式与Kubernetes ...

  5. 容器跨主机网络通信学习笔记(以Flannel为例)

    我们知道在Docker的默认配置下,不同宿主机上的容器通过 IP 地址进行互相访问是根本做不到的. 而正是为了解决这个容器"跨主通信"的问题,社区里才出现了很多的容器网络方案. 要 ...

  6. [书籍精读]《JavaScript异步编程》精读笔记分享

    写在前面 书籍介绍:本书讲述基本的异步处理技巧,包括PubSub.事件模式.Promises等,通过这些技巧,可以更好的应对大型Web应用程序的复杂性,交互快速响应的代码.理解了JavaScript的 ...

  7. Windows11 关闭搜索栏中的Web网页搜索

    ️ Win11 搜索栏总弹出网页搜索通过注册表彻底关闭 在 Windows 11 系统中,当你通过任务栏中的搜索栏查找内容时,除了显示本地文件.应用和设置外,系统还会自动集成 Bing 搜索结果,展示 ...

  8. 大模型VS小模型:论国产数据库运维AI Agent的正确打开方式

    作者:孙鹏,大衍(北京)科技有限公司研发工程师 首先为大家推荐这个 OceanBase 开源负责人老纪的公众号 "老纪的技术唠嗑局",会持续更新和 #数据库.#AI.#技术架构 相 ...

  9. Result Maps collection does not contain value for java.util.HashMap

    前言 查询报错 Result Maps collection does not contain value for java.util.HashMap 原因 SQL XML中包含此返回类型 解决 第一 ...

  10. Selenium框架

    Selenium框架 Selenium是一个自动化测试工具,用于模拟用户在Web应用程序上的操作.它提供了多种编程语言的接口,如Python.Java等,使测试人员能够编写自动化测试脚本.Seleni ...