深入理解JVM读书笔记一:Java内存区域

    概述:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,下图就是jvm运行时的状态。

                                                

具体划分为如下5个内存空间:

程序计数器:保证线程切换后能恢复到原来的执行位置

Java虚拟机栈(栈内存)为虚拟机执行java方法服务:方法被调用时创建栈帧-->局部变量表->局部变量、对象引用

本地方法栈:为虚拟机执使用到的Native方法服务

Java堆:存放所有new出来的东西

方法区:存储被虚拟机加载的类信息、常量、静态常量、静态方法等

运行时常量池:(方法区的一部分)

1、程序计数器:(线程私有)

        程序计数器是一块较小的内存空间,它可以看作是当前前程所执行的字节码的行号指示器,说的通俗一点,我们知道,Java是支持多线程的,程序先去执行A线程,执行到一半,然后就去执行B线程,然后又跑回来接着执行A线程,那程序是怎么记住A线程已经执行到哪里了呢?这就需要程序计数器了。因此为了线程切换后能够恢复到正确的执行位置,每条线程都有一个独立的程序计数器,这块儿属于“线程私有”的内存。

       如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。

2、Java虚拟机栈:(线程私有)

      每个方法被调用的时候都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表存放的是:编译期可知的基本数据类型、对象引用类型。 每个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。

       在Java虚拟机规范中,对这个区域规定了两种异常情况:

  (1)如果线程请求的栈深度太深,超出了虚拟机所允许的深度,就会出现StackOverFlowError(比如无限递归。因为每一层栈帧都占用一定空间,而 Xss 规定了栈的最大空间,超出这个值就会报错)

 (2)虚拟机栈可以动态扩展,如果扩展到无法申请足够的内存空间,会出现OOM

3、本地方法栈:(线程私有)

    (1)本地方法栈与java虚拟机栈作用非常类似,其区别是:java虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈则为虚拟机执使用到的Native方法服务。

    (2)Java虚拟机没有对本地方法栈的使用和数据结构做强制规定,Sun HotSpot虚拟机就把java虚拟机栈和本地方法栈合二为一。

    (3)本地方法栈也会抛出StackOverFlowError和OutOfMemoryError。

4、Java堆:即堆内存(线程共享)

    (1)堆是java虚拟机所管理的内存区域中最大的一块,java堆是被所有线程共享的内存区域,在java虚拟机启动时创建,堆内存的唯一目的就是存放对象实例几乎所有的对象实例都在堆内存分配。

    (2)堆是GC管理的主要区域,从垃圾回收的角度看,由于现在的垃圾收集器都是采用的分代收集算法,因此java堆还可以初步细分为新生代和老年代。

    (3)Java虚拟机规定,堆可以处于物理上不连续的内存空间中,只要逻辑上连续的即可。在实现上既可以是固定的,也可以是可动态扩展的。如果在堆内存没有完成实例分配,并且堆大小也无法扩展,就会抛出OutOfMemoryError异常。

5、方法区:(线程共享)

    (1)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    (2)Sun HotSpot虚拟机把方法区叫做永久代(Permanent Generation),方法区中最终要的部分是运行时常量池。

    (3)方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。在内存不足时抛出OutOfMemoryError异常。

6、运行时常量池:

    方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译;运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如String类的intern()方法,作用是String维护了一个常量池,如果调用的字符“abc”已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。

7、直接内存(Direct Memory)

      直接内存并不是JVM管理的内存,可以这样理解,直接内存,就是JVM以外的机器内存,比如,你有4G的内存,JVM占用了1G,则其余的3G就是直接内存,也可能出现OutOfMemoryError的异常。

8、GC对它们的回收:

        内存区域中的程序计数器、虚拟机栈、本地方法栈这3个区域随着线程而生,线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈的操作,每个栈帧中分配多少内存基本是在类结构确定下来时就已知的。在这几个区域不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟着回收了。

9、GC回收的主要对象

      而Java堆和方法区则不同,一个接口中的多个实现类需要的内存可能不同,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的。

猜你喜欢

转载自blog.csdn.net/qq_26891141/article/details/85011648