JVM方法逃逸

举报
上善若水. 发表于 2022/11/30 13:56:36 2022/11/30
【摘要】 三色标记三色标记,这三色就是白黑灰,白色表示对象不可达,黑色表示已经被访问过了,它关联的对象也扫描了,灰色就是还有一部分对象没有被扫描过。跨代引用跨代引用,年轻代中有一个对象被老年代的对象引用了,这个时候进行minor gc。正常我们的思路是,年轻代里面的对象被老年代里面的对象引用的话,就进行一个遍历,遍历老年代里面的对象。但是老年代里面的对象是很多的,遍历这个是很消耗性能的,这个时候jvm...

三色标记
三色标记,这三色就是白黑灰,白色表示对象不可达,黑色表示已经被访问过了,它关联的对象也扫描了,灰色就是还有一部分对象没有被扫描过。

跨代引用
跨代引用,年轻代中有一个对象被老年代的对象引用了,这个时候进行minor gc。正常我们的思路是,年轻代里面的对象被老年代里面的对象引用的话,就进行一个遍历,遍历老年代里面的对象。但是老年代里面的对象是很多的,遍历这个是很消耗性能的,这个时候jvm引入了一个记忆集的抽象数据结构。它用于记录从非收集区域指向收集区域的一个指针集合的抽象数据结构。比如说,我们在年轻代里面进行minor gc,它里面有一个记忆集,记录了老年代引用年轻代的对象的指针。如果记忆集里面有当前对象的引用,那么这个对象就不能被回收。

逃逸分析
逃逸分析原理:

逃逸分析有三种程度,从不逃逸,方法逃逸,线程逃逸,这三个由低到高表示不同逃逸的程度。

方法逃逸:分析对象动态作用域,当一个对象在方法里面定义之后,可能会被外部方法引用,比如作为参数传到其他方法里面去,这个叫方法逃逸。
线程逃逸:一个对象可能被外部线程访问到,比如可以赋值给其他线程能访问的实例变量,这个叫线程逃逸。

优化手段有三种:第一种是栈上分配,标量替换,锁清除(同步清除)。

栈上分配,java堆中的对象,对于各个线程都是共享可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据。虚拟机的垃圾收集子系统会回收堆中不再使用的对象,但是回收动作无论是标记筛选出可回收对象, 还是回收和整理内存,都需要耗费大量资源。如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分 配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧出栈而销毁。在一般应用中,完全不会 逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着 方法的结束而自动销毁了,垃圾收集子系统的压力将会下降很多。栈上分配可以支持方法逃逸,但不能支持线 程逃逸。

标量替换:一个数据已经无法再分解成更小的数据来表示了,Java虚拟机中的原始数据类型 (int、long等数值类型及reference类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。

一个数据可以继续分解,那它就被称为聚合量,Java中的对象就是典型的聚合量。如果把一个Java对象拆散,根据程序访问的情况,将其用到的成员变量恢复为原始类型来访问,这个过程就称为标量替换。

假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。

将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,很大机会被虚拟机分配至物理机器的高速寄存器中存储)分配和读写之外,还可以为后续进一 步的优化手段创建条件。标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配), 但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。

同步消除:线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不 会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以 安全地消除掉。

public String concatString(String s1,String s2,String s3){

StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3);

return sb.toString();

}
每个StringBuffer.append()方法中都有一个同步块,锁就是sb对象。 虚拟机观察变量sb,经过逃逸分析后会发现它的动态作用域被限 制在concatString()方法内部。也就是sb的所有引用都永远不会逃 逸到concatString()方法之外,其他线程无法访问到它,所以这里 虽然有锁,但是可以被安全地消除掉。在解释执行时这里仍然会 加锁,但在经过服务端编译器的即时编译之后,这段代码就会忽 略所有的同步措施而直接执行。

内存泄漏
内存泄漏:是指创建的对象已经没有用处,正常情况下应该会被垃圾收集器回收,但是由于该对象仍然 被其他对象进行了无效引用,导致不能够被垃圾收集器及时清理,这种现象称之为内存泄漏。

内存堆积
内存泄漏会导致内存堆积,最终发生内存溢出,导致OOM。 发生内存泄漏大部分是由于程序代码导致的,排查方法一般是使用 visualVM 进行heap dump,查看占用 空间比较多的 class 对象,然后检查该对象的instances 以及 reference引用,最终定位到程序代码。 如果堆内存比较大,进行head dump 产生的资源消耗不可接受,可以尝试使用轻量级的jmap生成堆转储快照 分析,思路与使用可视化工具一样。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。