拿午休时间来发了这篇博客,希望对大家有帮助
本文吸取《深入理解java虚拟机》与多篇博客精华详细解说了JVM中内存划分的情况。
大多数 JVM 将内存区域划分:
Method Area(Non-Heap)(方法区) ——线程共享
Heap(堆) ——线程共享
Program Counter Register(程序计数器) ——非线程共享
VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的)——非线程共享
Native Method Stack ( 本地方法栈 )——非线程共享
JVM运行的时候会分配好 Method Area(方法区) 和Heap(堆),而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。
非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行生命周期相同,
所以gc只发生在线程共享的区域(大部分发生在Heap上)的原因。
线程共享:
一、方法区
- 有时候也称为永久代(Permanent Generation),在该区内很少发生垃圾回收,在这里进行的GC主要是方法区里的常量池和类型的卸载
- 方法区主要用来存储已被虚拟机加载的类信息、常量、静态变量和即时编译后的代码等数据。
- 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。运行时生成的常量也会存在这个常量池中。比如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区的对象经历若干次收集仍然存活的,就会被转移到老年代。
堆中垃圾回收的时候注意一个大对象回收过程,描述如下:
年轻代与老年代控件分配如下:
Eden:80M
S1:10M
S2:10M
老年代:100M
过程:
- 新建60M的对象O1,此时Eden有足够内存吃下,所以60M被分配到Eden区。
- 再新建40M对象O2,此时因为S1和S2的控件不足以容纳下O1,所以O1被直接分配到老年代,而O2进入Eden区
- 清空O1、O2,新建90M对象O3,发现Eden不足以放下O3,O3被直接放入老年代中
- 清空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时拍摄一个“堆转储快照”,并将其保存在一个文件中。
线程私有:
一、虚拟机栈:
- 虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
- 局部变量表里存储的是基本数据类型和对象引用。局部变量所需的内存空间在编译器间确定
- 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。
-Xoss参数设置本地方法栈大小(对于HotSpot无效)
-Xss参数设置栈容量 例: -Xss128k
二、本地方法栈
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
三、程序计数器
代表着当前线程所执行字节码的行号指示器。
分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。
程序计数器是唯一一个java虚拟机规范没有规定任何OOM(Out Of Memory)情况的区域。