深入理解java虚拟机

来源:互联网 发布:优秀的短篇小说知乎 编辑:程序博客网 时间:2024/06/06 01:31

CGlib字节码技术

方法区溢出 -XX:PermSize=10M -XX:MaxPermSize=10M

本机直接内存溢出:

-Xmx20M -XX:MaxDirectMemorySize = 10M


public class DirectMemoryOOM {

  private static final int _1MB = 1024*1024;

  public static void main(String[] args) throws Exception {

    Field unsafeField = Unsafe.class.getDeclaredFields()[0];

    unsafeField.setAccessible(true);

    Unsafe unsafe = (Unsafe) unsafeField.get(null);

    while(true) {

       unsafe.allocateMemory(_1MB); // 真正申请分配内存的方法

    }

  }

}

引用计数算法

判断对象是否存活的算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1

根搜索算法

作用:判定对象是否存活,

不可到达GCRoots则会判定该对象是可回收的。

可作为GC Roots的对象包括: 虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、Native方法的引用对象

标记清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象

缺点:效率不高,空间问题因为标记清除后会产生大量不连续的内存碎片,有些大的对象需要分配连续的内存

复制算法

它将可用内存按容量划分成大小相等的两块,当这块内存用完了,就将存活的内存复制到另一块上,然后把已使用的内存空间一次清理掉

现在商业虚拟机都采用这种收集算法来回收新生代,

新生代和老年代


标记-整理算法

根据老年代特点,采取标记整理算法,标记过程和标记清除一样,但后续步骤是让存活的对象都向一端移动,然后清理掉端边以外的内存

分代收集算法

根据对象的存活周期不同将内存划分为几块,一般是把java堆分为新生代和老年代,在新生代中每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,

而在老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记清除或标记整理算法来进行回收。


serial收集器

特点:在进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束 stop the world

优点:简单、高效,因为专心做垃圾收集可以获得最高的单线程收集效率,在用户的桌面应用场景中,分配给虚拟机的内存一般不会很大,停顿时间可以控制在几十毫秒最多

一百多毫秒,这点停顿是完全可以接受的

对于运行在client模式下的虚拟机是一个很好的选择

parNew收集器

serial收集器的多线程版本,使用多条线程进行垃圾收集,也是stop the world机制

server模式下的虚拟机中首选的新生代收集器,目前只有它能与cms收集器配合工作,这块收集器是hotspot虚拟机中第一款真正意义上的并发收集器,

它第一次实现了让垃圾收集线程和用户线程同时工作


并发和并行的概念 当前垃圾收集器的上下文语境

并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态

并发:指的是用户线程与垃圾收集线程同时执行,但不一定是并行可能会交替执行,用户程序继续执行,而垃圾收集程序运行于另一个cpu上

parallel scavenge收集器

新生代收集器、可控制的吞吐量、控制吞吐量的参数:控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis、直接设置吞吐量大小的-XX:GCTimeRatio

、-XX:+UseApdativeSizePolicy 开关参数:打开后不需手工指定新生代的大小、GC自适应调节策略:只需把基本内存数据设置好如-Xmx设置最大堆,

然后使用最大停顿时间或吞吐量的参数给虚拟机设立一个优化目标

吞吐量=运行用户代码时间/cpu消耗总时间

GCTimeRatio(允许设置1-99的整数)1/(1+19)= 5%


serial old 收集器

这里面所说的client模式和server模式是什么意思?

parallel old收集器

是parallel scavenge收集器的老年代版本,使用多线程和标记整理算法

paraNew + cms组合

serial old + parallel scavenge组合 适合注重吞吐量和cpu资源敏感的场合

cms收集器 

Concurrent mark sweep 基于标记删除算法

4个步骤: 1.初始标记 2.并发标记 3.重新标记 4.并发清除 

1、3仍需stop the world 

2、4耗时最长

优点:并发收集、低停顿

缺点:1.占用一定cpu

   2.无法处理浮动垃圾可能出现concurrent mode failure失败而导致另一次full gc的产生

           3.收集结束时会产生大量空间碎片,空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,不得不提前触发一次full gc

     为了解决这个问题,cms收集器提供一个开关参数-XX:+UseCMSCompactAtFullCollection,用于在接受full gc服务后额外免费附送一个碎片整理过程,内存整理

             的过程是无法并发的,空间碎片没有了,但停顿时间变长了,-XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的full gc后,跟着来一次压缩

G1收集器

基于标记-整理算法实现的收集器,可以精确地控制停顿,有优先回收垃圾最多区域的能力


垃圾收集相关常用参数

UseSerialGC 虚拟机运行在client模式下的默认值,使用serial +serial old的收集器组合进行内存回收

UseParNewGC 使用ParNew + serial old组合

UseConcMarkSweepGC 使用ParNew + CMS + serial old的收集器组合进行内存回收,serial old收集器将作为CMS收集器出现concurrent mode failure失败后的后备收集器使用

UseParallelGC 虚拟机运行在server模式下的默认值,使用parallel scavenge + serial old的收集器组合

SurvivorRatio 新生代中eden区域与survivor区域的容量比值,默认为8

PretenureSizeThreshold 直接晋升到老年代的对象大小

MaxTenuringThreshold 晋升到老年代的对象年龄,每个对象在坚持过一次Minor GC之后年龄就加1

UseAdpativeSizePolicy 动态调整java堆中各个区域的大小以及进入老年代的年龄

HandlePrommotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survior区的所有对象都存活的极端情况

ParellelGCThreads 设置并行GC时进行内存回收的线程数

GCTimeRatio GC时间占总时间的比率,默认值为99 ,仅在使用Parellel Scavenge收集器时生效

MaxGCPauseMillis 设置GC的最大停顿时间,仅在使用Parellel Scavenge收集器时生效

CMSInitiatingOccupanyFraction 设置CMS收集器在老年代空间被使用多少后触发垃圾收集 默认68%

UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理

CMSFullGCsBeforeCompaction 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理



Minor GC:发生在新生代的垃圾收集动作,比较频繁 Major GC:老年代,速度缓慢

Eden是什么?新生代么 不是,是新生代的中的区域 ,对象优先在Eden分配,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC

Survivor空间 是什么?

-XX:+PrintGCDetails 收集器日志参数 打印内存回收日志 :这块日志晦涩难懂,需要研究下。

GC期间虚拟机又发现已有的3个2MB大小的对象全部无法放入survivor空间,所以只好通过分配担保机制提前转移到老年代去

在运行时通过-Xms20M、-Xmx20M和-Xmn10M 3个参数限制java堆大小为20M,其中10M分配给新生代,剩下10M分配给老年代,新生代

Eden区与一个survivor区空间比例是8:1,新生代总可用空间= Eden区 + 1个survivor区的总容量

tenured generation年老代

大对象直接进入老年代

-XX:PretenureSizeThreshold 大于这个数的对象直接在老年代中分配 单位需转换成B

只对serial 和 parNew收集器有效

private static final int_1MB = 1024* 1024;

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

长期存活的对象将进入老年代

虚拟机给每个对象定义一个对象年龄计数器

参数-XX:MaxTenuringThreshold

-Xmn10M 年轻代 那么年老代为10M