深入Java虚拟机笔记(二):垃圾收集器与内存分配策略

介绍

程序计数器、虚拟机栈、本地方法栈三个区域随线程而生而死,栈中的栈帧随方法的进入和退出执行着出栈和入栈,所以以上区域的内存分配和回收基本不用我们考虑了。而Java堆和方法区不一样,我们常说的分配和回收的内存就指这两个。


判断对象是否存活的算法

需要注意的是计数算法没有被用来管理内存,主要原因它没有很难解决对象之间的相互循环引用问题

1、根搜索算法(GC Roots Tracing)

思路:通过一系列名为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连(即对象不可达),说明该对象是不可用的,即使对象之间有关联,但对象到GCRoots不可达的,这也是不可用,是可回收的
这里写图片描述
可作为GC Roots的对象包括下面几种:
1、虚拟机栈(栈帧中本地变量表)引用的对象
2、方法区的类静态属性引用的对象
3、方法区的常量引用的对象
4、本地方法栈中JNI(Native方法)的引用的对象


对象的生存还是死亡

要真正宣布一个对象死亡,至少有两次的标记过程,当该对象被发现没有与GC Roots相连的引用链,会被第一次标记并且进行筛选,筛选的条件是此对象是否有必要执行finalize()方法。如果有,该对象会被放置F-Queue队列中,由虚拟机自动创建的Finalizer线程执行,这种执行是指虚拟机会触发这个方法,稍后GC会对F-Queue的对象进行第二次的标记,如果在对象在finalize()方法中重新与引用链的任何对象建立关联(比如把自己赋值给某个类变量或对象的成员变量),那么在第二次标记中它会被移除(注意自救只有一次,因为一个对象的finalize()方法最多会被系统调用一次)


回收方法区

如果没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,那么这个常量就是“废弃常量”,而判断类是否“无用的类”需要满足下面三个条件:
1、该类所有的实例都已经被回收,也就Java对不存在在该类的任何实例
2、加载改了的ClassLoader已经被回收
3、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

满足上面条件,说明可以被回收,而是否会被进行回收要看HotSpot虚拟机的控制参数

在大量使用反射,动态代理,CGLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出


垃圾回收的基本算法

标记压缩法

先从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清除未标记的对象,而是将所有的存活对象压缩到内存的一端之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。

标记回收法

从“GC Roots”(GC Roots指垃圾收集器的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象)集合开始,将内存整个遍历一次,保留所有被GC Roots直接或者间接引用到的对象,而剩下的对象都当作垃圾对待并回收,这个算法需要中断进程内其他组件的执行并且可能产生碎片化

复制回收法

将内存分为大小相等的两部分(假设A、B两部分),每次呢只使用其中的一部分(这里我们假设为A区),等这部分用完了,这时候就将这里面还能活下来的对象复制到另一部分内存(这里设为B区)中,然后把A区中的剩下部分全部清理掉。这样内存碎片的问题就解决了
这里写图片描述

分代回收法

根据对象的生命周期将内存划分,然后进行分区管理,在Java虚拟机分代垃圾回收机制中,应用程序可用的堆空间可以分为年轻代老年代,年轻代有被分为Eden区,From区与To区
这里写图片描述
分代回收法更详细链接

>与垃圾回收的附加选项

下面两个选项用于设置java虚拟机内存大小
-Xms :设置java虚拟机堆内存的最大容量如java -Xmx256m XxxClass
-Xms :设置java虚拟机堆内存的初始容量,如java -Xms128m XxxClass

下面选项都是关于java垃圾回收的附加选项

-xx:MinHeapFreeRatio =40 :设置java堆内存最小的空闲百分比,默认为40,如java -xx:MinHeapFreeRadio = 40 XxxClass

-xx:MaxHeapFreeRatio=70 :设置Java堆内存最大的空闲百分比,默认为70,如java -XX:MaxHeapFreeRatio =70 XxxClass

-xx:NewRatio=2 ;设置Yonng/Old内存的比例,如java -XX:NewRatio=1 XxxClass
-xx:NewSize=size:设置Yonng代内存的默认容量,如java -XX:Newsize=64m XxxClass

-xx:SurvivorRatio = 8;设置Yonng代中eden/survivor的比例,如java -xx:MaxNewSize=128m XxxClass

注意 当设置Young代的内存超过了-Xmx设置的大小时,Young设置的内存大小将不会起作用,JVM会自动将Young代内存设置为与-Xmx设置的大小相等。

XX:PermSIze=size;设置Permnanent代内存的默认容量,如java –XX:PermSize=128m XxxClass

-XX:MaxPermSize=64m;设置Permanent代内存的最大容量,如java -XX:MaxPermSize=128m XxxClass


垃圾回收器

串行回收器(Serial Collector)

单线程执行回收操作,回收期间暂停所有应用线程的执行,client模式下的默认回收器,通过-XX:+UseSerialGC命令可选项强制执行

并行回收器(Parallel Collector)

Server模式下的默认回收期,优点是使用多线程来扫描及压缩堆,缺点是不管执行的minor GC还是full GC都会暂停应用程序,适合容许暂停的应用,试图减少由回收器所引起的CPU开销

单线程串行回收 vs 多线程并行回收
考虑回收操作自身是否多线程处理的问题,单线程回收的优点是简单、易实现,碎片少,适用单核的机器。多线程并行回收在多核机器充分利用CPU资源,减少回收的时间,增加生产力,缺点是复杂还有可能产生碎片

并发标记清除回收器(Concurrent Mark-Sweep Collector)

这个算法使用多线程(Concurrent )来扫描并标记(Mark)那些不再使用的可以回收(Sweep )的对象
这个算法在两种情况下会进入“stop the world”模式

1、当标记永久代内存空间中的对象时;
2、当进行垃圾回收时,堆内存同步发生了一些变化。

缺点:会碰到promotion fail,指回收新生代及年老代时出现竞争条件的情况,为了确保不会发生这种情况,要么增加老生代的大小(或增加整个堆的大小),要么给回收器分配一些后台线程以便与对象分配的速度进行赛跑

还有缺点就与并行回收器比,使用CPU资源会更多,对于大多数长期运行的程序而言,应用的暂停对它们不利,这个时候可以使用CMS回收器。假设你的堆小于4G,而你希望分配更多的CPU资源以避免应用暂停,可以选择CMS回收器,若堆大于4G,可以选择G1回收器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值