读书笔记——深入理解JAVA虚拟机(1)

来源:互联网 发布:淘宝开直通车教程 编辑:程序博客网 时间:2024/06/05 14:25

第二章  JAVA内存区域与内存溢出异常 

本章主要讲解虚拟机内存的划分、各划分模块的作用以及可能出现的OOM。

2.2 运行时数据区域

JVM将其管理的内存划分为如下模块:

1、程序计数器:这块内存很小,主要用来记录当前线程执行的字节码指令的地址。由于JAVA的多线程是通过各个线程轮流交替占用CPU时间来实现的,因此就会有线程的切换,为了在线程切换后能够恢复到之前的执行位置,因此程序计数器是线程私有的。该内存区域不会发生OOM。

2、虚拟机栈:虚拟机栈代表了执行的线程。每当生成了一条线程,JVM就会对应的在该区域划分出一部分内存给这条线程使用,这部分内存就是虚拟机栈;线程每执行一个方法,对应的就会生成一个栈帧进入虚拟机栈中,方法结束,栈帧出栈。栈帧中存储着该方法的全部信息,如方法的参数、局部变量、返回值、方法出口等等。这部分信息占用的内存空间在编译时就已经确定,运行期不会改变。该区域会有两种OOM异常,1:StackOverFlow:线程请求的栈深度超过了最大值,如递归。2:OutOfMemory:虚拟机栈占用的内存空间超过了最大值。典型的如多线程。

虚拟机栈是线程私有的,其生命周期和线程一致。

3:堆:这块内存是JVM放置对象实例及数组的地方,JVM将这块区域分为新生代和老年代,新生代中还可以细分Eden,From Survivor, To Survivor。

这部分内存是所有线程共享的,但是也可以划分出一部分线程私有的内存,叫做TLAB(本地线程分配缓冲区)。当对象所需的空间超过该区域的最大值时,会发生OOM异常。

4、方法区:该部分是用来防止JVM加载的类信息、常量、静态变量等数据的。是线程共享的,会发生OOM。

5、运行时常量池:运行时常量池是方法区的一部分,Class文件中有一部分是常量池,保存的是字面量和符号引用,Class文件被加载后,这部分信息就会保存在运行时常量池中。

6、直接内存:这部分内存不归JVM管理,主要是NIO类使用,NIO通过DirectByteBuffer对象来直接操作操作系统内核管理的内存,减少了数据在操作系统内存和JVM内存之间来回复制的成本, 提高了I/O效率。

2.3 HotSpot虚拟机对象探秘

当遇到一条new指令的时候,在JVM内部会发生如下动作:

1、首先检查要实例化的类是否被加载过,如果没有被加载过,则必须先加载该类(懒加载)

2、如果已经加载了类,那么该对象在所需的空间大小就已经确定,JVM就会在堆内存为该对象分配空间,这时候根据JVM垃圾收集器(GC)的特性,有两种分配方式

2.1、如果GC使用标记-清理算法,则堆空间是不连续的,那么虚拟机会维护一个可用空间列表,列表中记录着堆内存中能用的空间,JVM根据这个列表找到能用的空间给对象,这叫做“空闲列表”。

2,.2、如果GC使用复制或标记-整理算法,则堆空间是连续的,那么虚拟机只需要将标志着可用和不可用分界点的指针朝不可用那里偏移出一个对象大小即可,这叫做“指针偏移”。

3、在堆上为对象分配了空间后,JVM会将该内存空间初始化为“零”值。

4、JVM设置对象头信息,如类的元数据、对象的Hash值等。

5、调用程序员在代码中指定的初始化方法,初始化该对象。

经过如上步骤、一个对象就在虚拟机中诞生了。