[置顶] JVM内存区域划分详解

时间:2021-06-27 20:50:43

拿午休时间来发了这篇博客,希望对大家有帮助
本文吸取《深入理解java虚拟机》与多篇博客精华详细解说了JVM中内存划分的情况。

大多数 JVM 将内存区域划分:

  1. Method Area(Non-Heap)(方法区) ——线程共享

  2. Heap(堆) ——线程共享

  3. Program Counter Register(程序计数器) ——非线程共享

  4. VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的)——非线程共享

  5. Native Method Stack ( 本地方法栈 )——非线程共享

JVM运行的时候会分配好 Method Area(方法区) 和Heap(堆)而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。
非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行生命周期相同,
所以gc只发生在线程共享的区域(大部分发生在Heap上)的原因。
[置顶]        JVM内存区域划分详解

线程共享
一、方法区

  1. 有时候也称为永久代(Permanent Generation),在该区内很少发生垃圾回收,在这里进行的GC主要是方法区里的常量池和类型的卸载
  2. 方法区主要用来存储已被虚拟机加载的类信息、常量、静态变量和即时编译后的代码等数据。
  3. 方法区里有一个运行时常量池用于存放静态编译产生的字面量和符号引用。运行时生成的常量也会存在这个常量池中。比如String类的intern()方法

扩展:

  • -XX:MaxPermSize设置上限
  • -XX:PermSize设置最小值 例:VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

二、堆
在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作
堆空间分为老年代和年轻代。刚创建的对象存放在年轻代,而老年代中存放生命周期长久的实例对象。年轻代中又被分为Eden区(圣经中的伊甸园)、和两个Survivor区(From Space和To Space)。新的对象分配是首先放在Eden区,Survivor区作为Eden区和Old区的缓冲,在Survivor区的对象经历若干次收集仍然存活的,就会被转移到老年代
[置顶]        JVM内存区域划分详解
堆中垃圾回收的时候注意一个大对象回收过程,描述如下:
年轻代与老年代控件分配如下:

  • Eden:80M

  • S1:10M

  • S2:10M

  • 老年代:100M

过程:

  1. 新建60M的对象O1,此时Eden有足够内存吃下,所以60M被分配到Eden区
  2. 再新建40M对象O2,此时因为S1和S2的控件不足以容纳下O1,所以O1被直接分配到老年代,而O2进入Eden区
  3. 清空O1、O2,新建90M对象O3,发现Eden不足以放下O3,O3被直接放入老年代中
  4. 清空O3,新建110M对象O4,O4发现Eden和老年代都没有足够控件,直接返回OutOfMemoryError

结论:

  • 当一个对象大于eden区而小于old区(老年代)时的时候会直接扔到old区
    而但对象大于old区时,会直接抛出OutOfMemoryError(OOM)

    扩展:
    参数-XX:PretenureSizeThreshold:这个参数的单位是Byte,
    其作用是当新对象申请的内存空间大于这个参数值的时候,直接扔到old区
    -Xms:设置最小值
    -Xmx:设置最大值 例:VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
    若-Xms=-Xmx,则可避免堆自动扩展
    -XX:+HeapDumpOnOutOfMemoryError
    JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中

线程私有
一、虚拟机栈:

  1. 虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
  2. 局部变量表里存储的是基本数据类型和对象引用。局部变量所需的内存空间在编译器间确定
  3. 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。

-Xoss参数设置本地方法栈大小(对于HotSpot无效)
-Xss参数设置栈容量 例: -Xss128k

二、本地方法栈
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。

三、程序计数器
代表着当前线程所执行字节码的行号指示器
分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成
程序计数器是唯一一个java虚拟机规范没有规定任何OOM(Out Of Memory)情况的区域。