资讯专栏INFORMATION COLUMN

《深入理解Java虚拟机第3版》垃圾收集器与内存分配策略、虚拟机性能监控故障处理工具

Kerr1Gan / 2983人阅读

摘要:目录往期博客课堂篇初识常量池简单理解字符串常量池静态常量池大整型常量池为什么要了解垃圾收集和内存分配如何判断对象已死引用计数算法可达性分析算法之后引用的扩充回收方法区垃圾收集算法分代收集理论标记清除标记复制标记整理对象分

目录


  1. 为什么要了解垃圾收集和内存分配?
  2. 如何判断对象已死?
    • 引用计数算法
    • 可达性分析算法
    • JDK1.2之后引用的扩充
  3. 回收方法区
  4. 垃圾收集算法分代收集理论
    • 标记清除
    • 标记复制
    • 标记整理
  5. 对象分配
  6. 虚拟机性能监控故障处理工具

1、为什么需要了解垃圾收集和内存分配?

当需要排查各种内存溢出、内存泄露问题时,当垃圾收集成为系统达到高并发量的瓶颈时,我们必须对这些“自动化”的技术实

施必要的监控和调节。

2、如何判断对象已死?

2.1、引用计数法

  • 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当有一个地方取消引用它时,计数器值减1
  • 虽然额外占用内存空间,但是他的原理简单,判定效率也很高
  • Java领域主流的虚拟机未采用没有选择用,因为这个看似简单的算法有很多例外的情况要考虑,必须配合额外的大量处理才能确保正确工作,如单纯的引用计数很难解决对象之间的互相引用问题。

2.2、可达性分析

  • 通过一系列的GC Roots的根对象作为起始点集,从这些结点开始,根据引用关系向下搜索,搜索走过的路径称为引用链,如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是不可达,证明这个对象不再被使用
  • 固定可以作为GC Roots的对象包括:
    • 虚拟机栈引用的对象(方法参数、局部变量、临时变量)
    • 方法区的静态属性引用的对象
    • 方法区的常量池引用的对象
    • 本地方法栈Native方法引用的对象
    • 虚拟机内部的引用
    • 同步锁synchrionized持有的对象

2.3、JDK1.2之后引用的扩充

四种新扩充的引用

  • 强引用:传统的"引用"定义如Object o = new Object(),只要强引用的关系还在,垃圾收集器就永远不会回收掉被引用的对象
  • 软引用:描述还有用,但是非必须的对象,在系统将要发生内存溢出异常前,会把这些对象列入回收范围之中进行二次回收,如果回收之后还是内存不够,就会抛出异常。JDK1.2版之后提供了SftReference类来实现软引用
  • 弱引用:也是描述那些非必须的对象,但是它的强度比弱引用更弱一些,被弱引用引用的·对象只能生存到下一次垃圾收集发生为止。
  • 虚引用:不对对象存活造成影响,位一各对象设置虚引用的目的只是为了垃圾收集器回收该对象时收到有个系统通知。

3、回收方法区

  • 相比于堆内存的回收,方法区的回收由于苛刻的回收条件,其区域垃圾收集的成果往往很低
  • 方法区的垃圾收集主要涉及两部分的内容:
    • 废弃的常量:字符串常量池里面的常量等
    • 不在使用的类型:类、接口、方法、字段的符号引用等
  • 判断一个类不在使用需要考虑
    • 该类的所有实例都已经被回收
    • 加载该类的类加载器已经被回收
    • 该类对java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
  • 在大量使用反射、动态代理、CGLIB等字节码的框架,动态生成JSP等这类频繁自定义类加载器的场景,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。

4、垃圾收集算法

垃圾收集算法可以划分为

  • 引用计数式垃圾收集(对应前面的 引用计数算法)
  • 追踪式垃圾收集(对用前面的 可达性分析算法)

分代收集设计原则

  • 弱分代假说:任何对象都是朝生夕灭
  • 强分代假说:熬过越多次垃圾收集的对象就越难消亡

因此存在

  • 部分收集Partical GC
    • 新生代收集Young GC
    • 老年代收集Old GC
    • 混合收集Mixed GC
  • 整堆收集Full GC

4.1、标记清除算法

算法分为 标记 和 清除 两个阶段

  • 标记:首先标记出所有要回收的对象(或者存活的对象),在标记完成后
  • 清除:统一回收掉被标记的对象

缺点

  • 执行效率不太稳定:如果Java堆中包含大量对象,而且其中大部分都是需要回收的,这时需要大量标记和清除操作
  • 内存空间碎片化问题:清除之后,产生大量不连续的内存空间,当需要分配大对象是,有可能放不下还需要进行一次GC

4.2、标记复制算法

相比标记清除算法

  • 半区复制:将内存容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就还将存活的对象复制到另外一块,再把使用过的一块内存全部回收清理。
  • 缺点:这种算法产生大量的内存空间复制的开销,可用内存缩小为原来的一半,空间浪费。

半区复制分代策略

  • Appel式回收:虚拟机的新生代使用此种回收算法,将新生代分为 Eden和Survivor区域,比例为8:1:1;每次将Eden的存活对象放到Survivor中
  • 需要老年代内存担保,也就是Survivor需要像老年代传送对象。

4.3、标记整理算法

相比于新生代使用标记复制算法、标记清除算法

  • 老年代不能使用标记复制算法,因为老年代对象存活率高,进行复制会浪费内存空间
  • 针对老年代对象的死亡特征,需要使用标记-整理算法,区别于标记-清除算法本质区别就是 不是直接对可回收对象清理,而是将存活的对象往一段移动,清除边界以外的内存。

特点

  • 移动存活对象的·时候,尤其是老年代,移动对象地址需要全部用户程序才能进行Stop The World
  • 不移动时候采用标记清除算法,内存分配复杂
  • 移动的时候采用标记整理算法,回收时更复杂
  • 有一种和稀泥方式,碎片化程度到达一定程度开启移动

5、对象分配

对象的内存分配从概念上讲都是堆上分配,(实际可能有即时编译后被拆散为标量类型并间接的在栈上分配),在经典分代的设计下,新生对象会直接分布在新生代,一些超过阈值的大对象可以直接分布在老年代

  • 对象优先在Eden分配:没有足够空间会触发YoungGC,存活的对象会进入Survivor
  • 大对象直接进入老年代:为了避免大对象内存复制的开销,直接将大对象分配到老年代,通过设置-XX:PretenureSizeThreshold=3145728参数指定阈值
  • 长期存活的对象进入老年代:虚拟机给每个对象定义了一个对象年龄(Age)计数器存储在对象头中,对象通常在Eden单上,如果第一次经理YongGC能够存活下来并且Survivor能够放的下,该对象就会被移动到Survivor并且年龄加1岁,当年龄增加到一定程度(默认15岁)就会被放到年代。可以通过-XXMaxTenuringThreshold设置
  • 动态对象年龄判断:除了对象年龄增长进入老年代,如果Survivor空间相同年龄的对象综合大于Survivor空间的一半,年龄大于等于该年龄的对象可以直接进入老年。
  • 空间分配担保:在发生YoungGC的时候,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,
    • 如果这个条件成立,那么这一次GC是确保安全的
    • 如果这个条件不成立,则虚拟机会检查参数-XX:HandlePromotionFailure参数是否允许担保失败,如果允许,则会检查老年代可用连续空间是否大于历届上升到老年代对象年龄的平均大小
      • 如果大于,进行一次YoungGC
      • 如果小于,或者是-XX:handlePromotionFailure设置不允许冒险,这时候就需要进行一次FullGC

6、虚拟机性能监控故障处理工具

6.1基础故障处理工具

  • jsp:虚拟机进程状况工具
    虚拟机进程查看定位工具

  • jstat:虚拟机统计信息监视工具
    显示类加载、内存、垃圾收集器、即时编译等运行时数据,定位虚拟机性能问题
    参数参考:https://blog.csdn.net/ouyang111222/article/details/53688986

  • jinfo:Java配置信息工具

  • jmap:Java内存映像工具
    用于生成堆转储快照

  • jhat:虚拟机堆转储快照分析工具

  • jstack:Java堆栈跟踪工具

  • jcmd:Java7开始提供的虚拟机诊断命令工具

基本Java工具

  • javadoc:API文档生成器
  • javap:字节码分析工具

6.2可视化故障处理工具

  • jhsdb:Java9开始提供的进程调试器,基于服务代理的调试工具
    首先使用jps查看进程号,然后jhsdb hsdb --pid xxx 进行操作
  • JConsole:Java监视与管理控制台
    控制台输入jconsole,本地、远程进行连接
  • VisualVM:多合-故障处理工具

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/119751.html

相关文章

  • 学习JVM必看书籍

    学习JVM的相关资料 《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》 showImg(https://segmentfault.com/img/bVbsqF5?w=200&h=200); 基于最新JDK1.7,围绕内存管理、执行子系统、程序编译与优化、高效并发等核心主题对JVM进行全面而深入的分析,深刻揭示JVM的工作原理。以实践为导向,通过大量与实际生产环境相结合的案例展示了解...

    shaonbean 评论0 收藏0
  • 深入理解Java虚拟》(四)虚拟性能监控故障处理工具

    摘要:虚拟机性能监控与故障处理工具详解概述本文参考的是周志明的深入理解虚拟机第四章,为了整理思路,简单记录一下,方便后期查阅。虚拟机堆转储快照分析工具功能用于分析生成的。 虚拟机性能监控与故障处理工具 详解 4.1 概述 本文参考的是周志明的 《深入理解Java虚拟机》 第四章 ,为了整理思路,简单记录一下,方便后期查阅。 JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的Vis...

    gself 评论0 收藏0
  • 深入理解Java虚拟》(一)Java虚拟发展史

    摘要:虚拟机发展史注本文大部分摘自深入理解虚拟机第二版作为一名开发人员,不能局限于语言规范,更需要对虚拟机规范有所了解。虚拟机规范有多种实现,其中是和中所带的虚拟机,也是目前使用范围最广的虚拟机。世界第一款商用虚拟机。号称世界上最快的虚拟机。 Java虚拟机发展史 注:本文大部分摘自《深入理解Java虚拟机(第二版)》 作为一名Java开发人员,不能局限于Java语言规范,更需要对Java虚...

    张春雷 评论0 收藏0
  • 深入理解Java虚拟》(三)垃圾集器内存分配策略

    摘要:当两个对象相互引用时,这两个对象就不会被回收引用计数算法不被主流虚拟机采用,主要原因是它很难解决对象之间相互循环引用的问题。 垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 《深入理解Java虚拟机》第三章 ,为了整理思路,简单记录一下,方便后期查阅。 3.2 对象已死吗 在垃圾收集器进行回收前,第一件事就是确定这些对象哪些还存活,哪些已经死去。 3.2.1 引用...

    Edison 评论0 收藏0
  • 摘记《深入理解Java虚拟:JVM高级特性最佳实践(第2)》

    摘要:第章内存区域与内存溢出异常运行时数据区域虚拟机在执行程序的过程中会把它所管理的内存划分为若干个不同的数据区域。即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 第2章 Java内存区域与内存溢出异常 2.2 运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。根据《Java虚拟机规范(Java SE 7版)...

    zoomdong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<