《深入理解Java虚拟机》读书笔记之自动内存管理机制

Java和C++之间有一堵由动态内存分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人却想出来。

  1. Java内存分区
    《深入理解Java虚拟机》读书笔记之自动内存管理机制
    由图中可以看到,JVM内存主要分为方法区,虚拟机栈,本地方法栈,堆,程序计数器。
    程序计数器(PC):是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器;是线程私有的;如果执行native方法,则pc的值为空,这块内存不存在内存溢出。

    Java虚拟机栈:是描述Java方法执行的内存模型,每个方法执行时都创建栈帧;是线程私有的的;这块内存存在*Error异常和OutOfMemoryError异常。

    本地方法栈:是描述native方法执行的内存模型;是线程私有的;没有规定native方法底层实现的语言类型,与Java虚拟机栈非常相似。

    Java堆:是存放对象实例的一块内存区域;是线程共享的;Java堆可以细分为,新生代和老年代,新生代可以细分为Eden空间,From Survivor空间,To Survivor空间;Java堆内存分区中最大的一块,只需要保证逻辑连续,不需要物理连续。(由于逃逸分析技术,存在一些对象在栈上分配)

    方法区:是存放已被虚拟机加载的类信息,常量,静态变量,JIT编译后的代码等(类似于C++的代码段,常量区,全局区);是线程共享的;也被称为永生代,但是也会GC;运行时常量池属于方法区的一部分,Java允许运行时将常量放入常量池,如String的intern()方法。

  2. 对象的创建
    Java创建对象的方式:new,clone,序列化,反射。

    Java对象内存分配方式:指针碰撞,空闲列表。

    Java对象分配的并发问题:CAS同步,使用TLAB(本地线程分配缓冲)解决。

    Java对象的内存布局:Header,Instance Data,Padding。Header主要有两部分信息,第一部分是Mark Wrod包括HashCode,GC分代年龄,锁标志状态,县城持有的锁,偏向线程ID,偏向时间戳(在偏向锁中会使用这部分信息),第二部分是指向类元数据的指针。

    Java对象的访问定位:句柄和直接指针。

  3. 如何判断一个对象需要被回收
    引用计数法,无法解决对象循环引用的问题
    可达性分析算法,GC Roots不可达的对象需要被回收
    GCRoots的选择:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,native方法引用的对象。

  4. Java引用分类
    强引用:一般使用的都是强引用,只要在使用永不会被回收
    软引用:当一次GC之后内存仍然不够,会被第二次GC回收
    弱引用:下次GC就会被回收
    虚引用:和GC无关,也无法通过虚引用获取对象实例

  5. finalize方法
    由于对象在GC过程中,可能出现自我拯救,所以finalize()何时调用无法确定,不要使用,替换为try-finally,和C++的析构函数有区别。

  6. GC算法(一般指堆的GC)
    标记 -清除算法:效率低,而且会产生内存碎片
    标记-整理算法:可以避免产生内存碎片
    复制算法:将存活的对象复制To Survivor中,然后Eden和From Survivor全部回收,会浪费部分内存
    分代收集算法:新生代由于朝生夕死,采用复制算法,老年代对象存活率高,采用标记-整理或标记-清理算法

  7. 方法区GC
    常量GC:没有引用就会被回收
    类的GC:满足所有的实例已被回收,该类的ClassLoader已被回收,且Class没有被引用才可能被回收

  8. 常见垃圾收集器
    Serial收集器:在垃圾回收的时候,必须暂停其他所有的工作线程,很影响用户体验,但是简单高效,新生代收集器。

    ParNew收集器:是Serial收集器的多线程版本,其他都是一样,但是目前只有它能和CMS收集器配合工作,新生代收集器。

    Parallel Scavenge收集器:采用复制算法实现的新生代收集器,相比于ParNew收集器,关键创新在于自适应调节策略,但是无法与CMS收集器配合使用。

    Serial Old收集器:Serial收集器的老年代版本,可以与Parallel Scavenge收集器搭配,也可以作为CMS的备选方案。

    Parallel Old收集器:Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法,在jdk1.6之后可以配合Parallel Svavenge收集器使用。

    CMS收集器:是一种基于标记清除以获取最短回收停顿时间为目标的收集器。工作过程为初始标记,并发标记,重新标记,并发清除。用户线程只会在初始标记和重新标记的时候停止,时间比并发标记时间远短。主要缺点是对cpu资源非常敏感,无法处理浮动垃圾,标记-清除产生大量内存碎片。
    《深入理解Java虚拟机》读书笔记之自动内存管理机制
    G1收集器:是一款基于标记-整理算法,并行,分代收集,且可预测停顿的垃圾收集器。G1打破了原来的内存布局,将内存分为多个大小相等的独立的Region,优先回收价值最大的Region。使用Remembered Se来保存一个Region里所有对象的被引用情况,保证扫描不会遗漏。GC流程为初始标记,并发标记,最终标记,筛选回收。虽然G1收集器对停顿时间有进一步优化,但是在吞吐量方面却表现不是很好。
    《深入理解Java虚拟机》读书笔记之自动内存管理机制

  9. 内存分配策略

    1. 对象优先在Eden区分配,如果Eden区没有足够的内存分配会发起一次Minor GC。
    2. 大对象直接进入老年代,为了避免Eden区和两个Survivor之间发生大量内存复制。
    3. 长期存活的对象进入老年代,每个对象定义一个Age,熬过一次Minor GC年龄加一。
    4. 动态对象年龄判定,如果Survivor空间中相同年龄所有对象的大小总和大于Survivor空间的一半,年龄大于该年龄的对象就可以直接进入老年代,不需要等到MaxTenuringThreshold
    5. 空间分配担保策略,为了保证Minor GC的安全,会检测老年代的连续空间是否大于新生代对象总大小和历次晋升的平均大小,小于会晋升,大于则进行Full GC(Full GC的速度比Minor GC慢10倍以上,所以要尽量避免Full GC)
  10. 常见JDK监控和故障排查工具
    jps:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程
    jstat:JVM Statistics Monitoring Tool,用于收集HotSpot虚拟机各方面的运行数据
    jinfo:Configuration Info for Java,显示虚拟机配置信息
    jmp:Memory Map for Java,生成虚拟机的内存转储快照(即headdump文件)
    jhat:JVM Head Dump Browser,用于分析heapdump文件
    jstack:Stack Trace for Java,显示虚拟机的线程快照

这部分主要描述了JVM对于内存的管理,分配,回收等基本内容和策略。