Day01
第2章 Java内存区域与内存溢出异常
2.2 运行时数据区域
2.2.1 程序计数器
▶️字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要这个计数器完成。
2.2.2 虚拟机栈
▶️是线程私有的,它的生命周期与线程相同,其描述的是Java方法执行的内存模型。每个方法执行时都会创建一个“栈帧”,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个虚拟机栈的入栈出栈的过程。 (:з」∠)_
局部变量表存放了各种基本数据类型、对象引用。64位长度的long和double占用2个局部变量空间(Slot),其余类型只占用1个。
在Java虚拟机规范中,对这个区域规定了两种异常情况:
1、如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
2、如果虚拟机栈可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常;
2.2.3 本地方法栈
为虚拟机使用到的native方法服务。也会抛上述两种异常。
2.2.4 Java堆
▶️Java堆是被所有线程共享的一块内存区域,在虚拟机创建时启动。存放对象实例,也是垃圾收集器管理的主要区域。如果堆中没有内存完成实例分配,并且堆也无法再扩展时,会抛出OutOfMemoryError异常。
✒️分配内存:
if(内存归整){
用过的内存放一边,没用的在另一边,内存写入时,移动分界点位置的指针,完成内存分配。---指针碰撞
}
if(内存不归整){
维护一个列表,记录哪些内存块是可用的,分配的时候找一块足够大的空间分划给对象,并更新表上的记录。---空闲列表
}
2.2.5 方法区
▶️存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
当方法区无法满足内存分配时,会抛出OutOfMemoryError异常。
2.2.6 运行时常量池
▶️是方法区的一部分,字面量和符号引用在类加载后进入方法区的运行时常量池中存放,具有动态性。在常量池无法申请到内存时会抛出OutOfMemoryError异常。
2.3 HotSpot虚拟机对象探秘
2.3.1 对象的创建
▶️虚拟机遇到new指令时,首先检查指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号是否已被加载、解析和初始化过,如果没有,就先执行类加载过程。->通过检查后,分配内存(指针碰撞、空闲列表)。
2.3.2 对象的内存布局
对象在内存中存储的布局分为3块区域:对象头、实例数据和对齐填充。
2.3.3 对象的访问定位
Java程序通过栈上的reference数据来操作堆上的具体对象。对象的访问方式目前主流的方式是使用句柄和直接指针两种。
☑️使用句柄访问
Java堆中会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体信息。
☑️直接指针访问
Java堆中的布局就必须考虑如何放置访问类型数据的相关信息。而reference中存储的直接就是对象地址。
第3章 垃圾收集器与内存分配策略
3.1 对象已死吗
3.2.1引用计数算法
▶️给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器就减1。任何时刻计数器为零的对象就是不可能再被使用的。该算法存在缺陷,并不是虚拟机来判断对象是否存活的方法。
3.2.2 可达性分析算法
▶️算法基本思路是通过一系列称为 “GC Root” 的对象作为起始点,从这些节点向下搜索,搜索走过的路就是通过一系列称为引用链,当一个对象到GC Root没有任何引用链相连(对象不可达),则证明此对象是不可用的,需要被回收。
3.2.3 再谈引用
JDK1.2之后,Java对引用的概念进行了扩充。分为强引用、软引用、弱引用、虚引用。
🥇□强引用
代码中普遍存在,类似Object obj = new Object()
,只要强引用存在,垃圾收集器永远不会回收掉被引用的对象。
🥈□软引用
描述一些还有用但是非必须的对象。系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常,在1.2之后,提供了SoftReference类来实现软引用。
🥉□弱引用
也是用来描述非必须的对象,被弱引用关联的对象智能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否够用,都会回收掉只被弱引用关联的对象。1.2之后提供了WeakReference类实现弱引用。
✴️□虚引用
3.2.4 生存还是死亡
关于finalize()方法,对象在被回收前会对自己进行一次自救。
3.4 HotSpot的算法实现
3.4.1 枚举根节点
挨个通过GC Root寻找引用链,存在系统停顿。
3.4.2 安全点
。。。。。。。。。。。。
3.4.3 安全区域
安全区域指在一段代码片段中,引用关系不会发生改变,在这个区域中的任意地方开始GC都是安全的。也可以把Safe Region 看做是被扩展了的Safepoint。
3.5 垃圾收集器 (:з」∠)_
3.5.1 Serial收集器
✒️Serial收集器是最基本、发展历史最久的收集器。单线程,而且在收集工作进行时必须暂停其他所有工作线程,直到收集结束。但是简单高效,运行在新生代垃圾收集的区域,对于在Client模式下的虚拟机来说是一个很好的选择。
3.5.2 ParNew收集器
✒️ParNew收集器是多线程版本的Serial收集器。在控制参数、收集算法、Stop The World、对象分配规则、回售策略与Serial收集器相同。适合Server模式的新生代收集器。目前,除了Serial收集器,只有ParNew收集器可以跟CMS收集器配合。
3.5.3 Parallel Scavenge收集器
✒️新生代复制算法收集器。侧重点在达到一个可控制的吞吐量(CPU运行用户代码所耗时间/CPU消耗总时间)。
3.5.4 Serial Old收集器
✒️是Serial老年代的版本。单线程,使用标记-整理算法。适合Client模式下的虚拟机,但如果在Server模式下,还有两大用途:在JDK1.5以前的版本中与Parallel Scavcenge收集器搭配使用;另一种就是CMS的后备预案
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!