Java进阶内存布局与虚拟机内容

本篇文章的思维导图.png

java内存区域相关

  • java引用

  1. 强引用 类似 Object o = new Object();这类的引用。只要引用还在,垃圾回收器就不会回收掉被引用的对象。

  2. 软引用 SoftReference类 只有在内存不足的时候JVM才会回收该对象 在系统即将发生内存溢出异常之前,会将这些对象列入回收范围。 应用场景: java里面一般你可以用于图像的bitmap流,特别是照片很多的时候。

  3. 弱引用 WeakReference类 当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象 应用场景:如果一个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来记住此对象。

4.虚引用 PhantomReference类 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个。一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例。

  • 判断对象已死算法

  1. 引用计数法 缺点:如果有A跟B,A引用B,B引用A就会造成判断对象没死。
  2. 可达性分析算法
  • HotSpot虚拟机中对象的内存布局

1.  对象头 Header

1.1.  Mark Word

存储自身对象的运行时数据(eg:哈希码(hashcode)),GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等 这部分数据在长度为32位虚拟机跟64位的虚拟机中分别是32bit 64bit

1.2. 类型指针

对象指向它的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。(注意一点的是:如果对象是java数组,那么对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通的java对象的元数据确认java对象大小但是从数组的元数据中却无法确定数组的大小)

image.png

2. 对齐填充 padding

不是必然存在,也没有特别的含义。仅仅作占位符的作用。 因为HotSpot VM的自动内存管理系统要求对象的其实地址必须是8字节的整数倍。而对象头刚好是八的倍数 因此,当对象的实例数据部分没有对其时,它用来填充补全。

3. 实例数据

真正对象存储的有效信息,也是对程序代码中所定义的各种类型的字段内容。父类继承下来的或者子类定义的都需要记录。存储的顺序受 虚拟机的分配策略参数跟字段在java源码中定义的顺序影响。

  • 对象的访问定位

1. 句柄访问

好处:存储稳定的句柄地址,在对象别移动的时候(垃圾收集时移动对象很普遍)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

image.png

1. 直接指针访问

好处:最大的好处就是快,直接指向对象实例数据(对象实例数据再优质镇)。节省了一次对象的指针的定位

image.png

  • 垃圾收集算法(方法论)

  1. 标记 - 清除算法

分为 标记跟清除阶段:标记要收回收的对象,然后统一回收。 缺点: 1 效率问题 标记跟清除两个过程效率都不高; 2 标记清除后,产生大量不连续的内存碎片; 3 空间碎片导致分配较大内存对象的时候,无法找到足够的内存连续空间导致提前另外触发另一次垃圾收集动作。

  1. 复制算法

一般商业虚拟机使用这种收集算法来回收新生代。 一般将其分为 Eden : survivor : survivor 8:1:1 一般使用 Eden 跟其中一个survivor,等到回收的时候将存活的对象复制到没有使用的survivor内存区域。 有时候不能保证10%的survivor内存足够,依赖于其他的内存(老年代)进行分配担保 3. 标记 - 整理算法

跟标记清楚算法差不多,但是第二步骤不是清楚,而是移动,将所有存活的对象向一边移动,然后清空其他的内存。 4. 分代收集算法

根据对象存货周期不同将内存进行划分为几块。 一般划为:新生代跟老年代。 新声代:有大批对象死去,只有少量存活,选用复制算法。(只需付出少量存活对象的复制成本就可以完成收集) 老年代:对象存活率高,没有二外的空间对它进行担保。就必须使用标记 - 整理算法 或者 标记 - 清除算法

  • 垃圾收集器(具体实现)

七种垃圾收集器的 连线上的可以互相搭配

新生代收集器:

  1. Serial 最基本最悠久的收集器 采用复制算法  单线程的收集器 使用一个CPU或者一条线程去完成垃圾收集工作。而是进行垃圾收集的时候,必须停止其他所有的工作线程。 适用于 Client模式下的虚拟机使用

  2. ParNew 是Serial的升级版,使用多线程版本。(多CPU环境下的操作,单CPU情况下不会比Serial好,并且比较差。)属于并行的多线程收集器。适用于 很多再Service模式下的虚拟机中首选的新生代收集器(用它主要是它跟CMS可以搭配使用,考虑到CPU多相比Serial可以提高效率) (注意并行Parallel跟并发Concurrent的区别)

  3. Parallel Scavenge 采用复制算法 注重吞吐量 即:运行代码的时间/(运行代码的时间+垃圾收集时间)  适用于 后台运算而不需要太多交互的任务

老年代收集器:

  1. Serial Old(MSC) Serial收集器的老年代版本。采用标记-整理 算法 主要是给Client模式下的虚拟机使用。两大用途:1 jdk1.5以及以前版本中与配合Parallel Scavenge搭配使用 2作为CMS的后备预案**

  2. Parallel Old Parallel Scavenge的老年代本 使用多线程和标记-整理算法 jdk1.6后才提供的。 在注重吞吐量以及CPU资源敏感的场合可以优先选用 新生代 Parallel Scavenge+老年代 Parallel Old**

  3. CMS Concurrent Mark Sweep 获取最端回收停顿时间为目标的收集器。很大一部分的java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其注重服务器的响应速度。 基于标记-清除法 比之前的收集器更加复杂一些。包括四个步骤:初始标记 并发标记 重新标记 并发清除(三大缺点:1对CPU资源敏感;2无法处理浮动垃圾(标记后线程运行继续产生新的垃圾),只能交给下此GC处理 3 基于 标记-清除法,导致收集结束后会有大量的空间碎片产生,往往出现老年代还有很多的大量空间碎片产生,无法找到足够的内存而重新触发GC)

另外还有一个是新生代跟老年代存在的收集器:GI

image.png

参考: 《深入理解Java虚拟机》 部分网上文章 如有侵权请联系本人

猜你喜欢

转载自juejin.im/post/5dbeeeb9f265da4d307f14c7