一、如何判定对象为垃圾对象

-verbose:gc  打印垃圾回收简单信息参数
-xx:+PringDCDetail 打印垃圾回收的详细信息

引用计数法

引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

采用引用计数的垃圾收集机制中,垃圾收集的开销被分摊到整个应用程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。

引用计数算法有一个比较大的问题,那就是它不能处理环形数据,即如果有两个对象相互引用,那么这两个对象就不能被回收,因为它们的引用计数始终为1。这也就是我们常说的“内存泄漏”问题。

Python中采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

可达性分析法

为了引用计数算法中循环引用导致垃圾不会被回收的问题,在Java中采取了 可达性分析法。同样采用此法的还有C#、Lisp(最早的一门采用动态内存分配的语言)。该方法的基本思想是通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。

GC Roots的对象

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)
  • 方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)
  • 方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象)
  • 本地方法栈中(Native方法)引用的对象(可以理解为:引用Native方法的所有对象)

二、如何回收

JVM的内存分代划分

Java虚拟机将堆内存划分为新生代老年代永久代。

新生代(Young)

HotSpot将新生代划分为三块,一块较大的Eden空间和两块较小的Survivor空间,默认比例为8:1:1。划分的目的是因为HotSpot采用复制算法来回收新生代,设置这个比例是为了充分利用内存空间,减少浪费。新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代),当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。

老年代(Old)

在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。

永久代(Permanent)

永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收。

回收策略

1.标记-清除算法(Mark-Sweep)

算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

标记-清除算法的主要缺点:

  • 效率问题:标记和清除过程的效率都不高;
  • 空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,碎片过多会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World。

2.复制算法(Copying)

为解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
它的主要缺点有两个:

  • 效率问题:在对象存活率较高时,复制操作次数多,效率降低;
  • 空间问题:內存缩小了一半;需要使用老年代的額外空间做分配担保。

 From SurvivorTo Survivor使用的就是复制算法。老年代不使用这种算法。

3.标记-整理算法

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。    
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。也称为标记-整理-清除算法。

4.分代收集算法(Generational Collection)

GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。
“分代收集”算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

参考资料:

  1. https://www.jianshu.com/p/1d5fa7f6035c
  2. https://blog.csdn.net/luzhensmart/article/details/81431212
  3. https://blog.csdn.net/wuzhiwei549/article/details/80563134
  4. https://www.cnblogs.com/fangfuhai/p/7206944.html

Java虚拟机-垃圾回收简介相关推荐

  1. JAVA虚拟机垃圾回收机制和JAVA排错三剑客

    一.Java虚拟机逻辑回收机制 1.Java垃圾回收器 Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的自动分配(Memory Al ...

  2. Java虚拟机垃圾回收相关知识点全梳理(下)

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 上一篇文章<Java虚拟机垃圾回收相关知识点全梳理(上)>我整理分享了JVM运行时数据区域的划分,垃圾判 ...

  3. 了解java虚拟机—垃圾回收算法(5)

    引用计数器法(Reference Counting) 引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器减1.只要对象A的引用计数器的 ...

  4. Java虚拟机 —— 垃圾回收机制

    在Java虚拟机中,对象和数组的内存都是在堆中分配的,垃圾收集器主要回收的内存就是再堆内存中.如果在Java程序运行过程中,动态创建的对象或者数组没有及时得到回收,持续积累,最终堆内存就会被占满,导致 ...

  5. JAVA虚拟机垃圾回收算法原理

    除了释放不再被引用的对象外,垃圾收集器还要处理堆碎块.新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活动的对象之间.请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的总空 ...

  6. 深入理解Java虚拟机垃圾回收机制

    文章目录 什么是垃圾回收 哪些内存需要被回收?什么时候回收?如何回收? 哪些内存需要被回收?什么时候回收? 引用计数算法 可达性分析算法 如何回收?(垃圾收集算法) 标记-清除算法 复制算法 标记-整 ...

  7. 老生常谈Java虚拟机垃圾回收机制(必看篇)

    垃圾收集 垃圾收集主要是针对堆和方法区进行. 程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收. 判断一个对 ...

  8. java虚拟机垃圾回收被误解的7件事

    对Java垃圾回收最大的误解是什么?它实际又是什么样的呢? 当 我还是小孩的时候,父母常说如果你不好好学习,就只能去扫大街了.但他们不知道的是,清理垃圾实际上是很棒的一件事.可能这也是即使在Java的 ...

  9. 一篇文章教你弄懂java CMS垃圾回收日志

    文章目录 一.CMS垃圾回收器介绍 二.CMS JVM运行参数 三.CMS收集器运行过程 1.初始标记(CMS initial mark) 2.并发标记(CMS concurrent mark) 3. ...

最新文章

  1. 安全的加强的linux:SELinux
  2. nyoj 55 懒省事的小明
  3. stm32之iap实现应用(基于串口,上位机,详细源码)
  4. 高一计算机组装,高中生计算机组装与维护,呼市新华互联网学校
  5. 如何设计一个合适的系统电源
  6. cstring判断包含字符串_Python字符串方法之-解决判断问题
  7. OpenCV_ cv2.imshow()
  8. python 速度 memmap_从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例
  9. java web学习总结(二十一) -------------------模拟Servlet3.0使用注解的方式配置Servlet...
  10. 03_java基础(四)之方法的创建与调用
  11. c++生成随机数_C 语言产生随机数的方法
  12. 人肉搜索将被禁止,大家要保护好自己的个人信息!
  13. 研发项目wbs分解简单案例_2013项目管理案例分析:工作分解结构(WBS)(精选五篇)...
  14. 漂亮好听的蓝牙小音箱,真是郊游好玩伴,Sanag M11体验
  15. H5页面唤起微信等app
  16. 声势浩大发展云服务的金蝶,如今“破茧”了吗
  17. 使用 brew 安装mongodb
  18. 卸载 Mac 默认的 Xcode 附带的 git
  19. 全志VR9虚拟现实专用芯片处理器介绍
  20. virtualbox E_INVALIDARG (0x80070057) 和 E_FAIL (0x80004005) SessionMachine

热门文章

  1. JQuery EasyUI之DataGrid列名和数据列分别设置不同对齐方式(转)
  2. SqlServer为什么自动在主键上建立聚集索引
  3. 一个关于文件结构体数组的条件排序函数
  4. c语言宏定义_C语言宏定义
  5. golang channel的一些总结
  6. go map详细使用方法
  7. python--lambda
  8. centos6.5搭建git服务器 win10 安装git客户端连接 和遇到的问题+解决方式
  9. SpringBoot原理-SpringBoot核心运行原理
  10. 'utf8' codec can't decode byte --python