Java虚拟机内存管理
2021-05-06 14:20:41 8 举报
AI智能生成
java虚拟机内存管理机制内容梳理
作者其他创作
大纲/内容
1. Java内存区域与内存溢出异常
1. 运行时数据区域
1. 线程共享
方法区
用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
堆
存放对象实例,几乎所有的对象实例都在这里分配内存
Java堆是垃圾回收器管理的内存区域
2. 线程不共享
Java虚拟机栈
生命周期与线程相同,虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧。每一个方法被调用直至执行完毕的过程,就对应这一个栈帧在虚拟机中从入栈到出栈的过程
本地方法栈
与虚拟机栈的作用十分相似,只不过本地方法栈针对的是本地(native)方法
程序计数器
在Java虚拟机的概念模型中,字节码解释器工作室就是通过改变这个计数器的值来选取下一条所需要执行的字节码指令,他是控制流程的指示器,分支、循环、跳转、异常处理、线程回复等基础功能都是需要依赖程序计数器完成。
是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器
3. 非虚拟机运行时数据区部分——直接内存
JDK1.4之后引入NIO类。引入了一种基于通道(Channel)与(Buffer)的I/O方式,他可以使用Native函数直接分配堆外内存,然后通过存储在Java堆里面的DirectByteBuffer对象作为这一块内存的引用进行操作。这样能在一些场景中显著的提高性能,应为避免了在Java堆和Native堆中来回复制数据
2. HotSpot虚拟机
1. 对象的创建
1. 检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析、初始化过。如果没有则必须执行相应的类加载过程
2. 在类加载检测通过之后,接下来虚拟机就会为新生对象分配内存
如果Java堆中内存是绝对规整的,所有被使用的内存放在一边,空闲的内存放在一边,中间放着一个指针作为分解点的指示器,那所分配内存就仅仅是把那个指针向空闲方向挪动一段与对象大小相等的距离。这种分配方式称为“指针碰撞”
如果Java堆中的北村并不是规整的,已被使用的内存和未被使用的内存相互交错在一起,那就没有办法简单的进行指针碰撞了,虚拟机就必须维护一个列表,记录那些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。这种分配方式称为“空闲列表”
3. 内存分配完成之后,虚拟机必须将分配到的内存空间(不包括对象头)都初始化为零值,如果使用了TLAB(Thread Local Allocation Buffer, 本地线程分配缓冲)初始化零值这一项工作可以提前到TLAB分配时顺便进行。这步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,是程序能反问道这些字段的数据类型所对应的零值。
4. Java虚拟机会设置对象头,包括有:这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希码(哈希码会延后到真正调用Object::hashCode()方法时才会计算)、对象的GC分代年龄信息。
5. 以上四步完成之后,从Java虚拟机的角度出发一个新的对象已经产生,但从Java程序的角度来讲,对象创建猜刚开始——构造函数
2. 对象的内存布局
对象在堆中内存可以划分为三个部分:
1. 对象头(Header):分为两类信息
1. 用于存储对象自身的运行时数据
2.类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该类对象是那个类的实例
2. 实例数据(Insatnce Data)
对象真正存储的有效信息,即程序代码中所定义的各种类型的字段内容,无论是在父类中继承下来的,还是仔之类中定义的字段都必须记录起来
3. 对齐填充(Padding)
占位符的作用
3. 对象的访问定位
1. 句柄访问
Java堆中可能会划分出一块内存作为句柄池, reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息
2. 直接指针访问
Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference中存储的就直接是对象地址
使用句柄访问的方式最大的好处就是reference中存储的是稳定句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要被修改
使用直接指针的方式最大的好处是速度快,少一次指针定位的指针开销
2. 垃圾收集器与内存分配策略
1. 判断对象死亡
1. 引用计数算法
2. 可达性分析
GC Roots
1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象
2. 在方法区中类静态属性引用的对象
3. 在方法区中常量引用的对象:字符串常量池里的引用
4. 在本地方法栈中JNI(Native方法)引用的对象
5. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象、还有系统类加载器
6. 所有被同步锁(synchronized关键字)所持有的对象
7. 反应Java虚拟机内部情况的JMXBean, JVMTI中注册的回调、本地代码缓存
2. 垃圾回收算法
1. 分代收集理论
1. 部分收集(Partial GC): 指目标不是完整收集整个Java堆的垃圾收集
1. 新生代收集(Minor GC/Young GC): 指目标只是新生代的垃圾收集
2. 老年代收集(Major GC/Old GC): 指目标只是老年代的垃圾收集,CMS收集器会有单独收集老年代的行为。
3. 混合收集(Mixed Gc):指目标是收集整个新生代以及部分老年代的垃圾收集。G1收集器会有这种行为
2. 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集器
2. 标记-清除算法
1. 标记出所有需要回收的对象
2. 标记完成之后清除所有被标记的对象
3. 标记-复制算法
概念: 将内存按容量划分为大小相等的两块,每次使用其中一块。当这块空间的内存快用完时,就将还存活着的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。
4. 标记-整理算法
1. 标记出所有需要清除的对象
2. 让所有所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
3. Hotspot的算法实现细节
4. 经典垃圾回收器
1. Serial收集器
单核单线程处理,它工作时必须暂停掉其他所有工作线程
2. ParNew收集器
Serial的多线程并行版本, 可与CMS收集器配合工作
3. Parallel Scavenge收集器
达到一个可控制的吞吐量
4. Serial Old收集器
Serial收集器的老年代版本,他同样是一个单线程收集器,使用标记-整理算法
Parallel Old收集器
6. CMS(Concurrent Mark Sweep)收集器
是一种以获取最短回收停顿时间为目标的收集器。
1. 初始标记 CMS initial mark
2. 并发标记 CMS concurrent mark
3. 重新标记 CMS remark
4. 并发清除 CMS concurrent sweep<br>
7. Garbage First收集器
判断那个区域的垃圾数量最多,回收收益最大
1. 初始标记
标记GC ROOTs能直接关联到的对象
2. 并发标记
从GC root开始对堆中对象进行可达性分析,递归扫描整个堆里面的对象图,找到要回收的对象,耗时较长但可与用户线程并发执行。
3. 最终标记
对用户线程做一个短暂的暂停,用于处理并发阶段结束后仍留下来的最后少量的要回收对象
4. 筛选回收
更新Region的统计数据,对各个Region的回收价值成本机型排序,根据用户所期望的停顿时间来制定回收计划。可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,在清理掉整个旧Region的全部空间
3. 虚拟机性能监控、故障处理工具
4. 调优案例分析
收藏
0 条评论
下一页