运行时数据区域包括如下几个区域:
1、程序计数器
英文为 Program Counter,简称 PC,此内存区域存储指令位置,为每个线程独占。
取PC中的值,找到对应位置的指令,然后执行该指令,然后将PC++。
2、堆
Heap,java 线程间共享的一块内存区域,所有对象实例以及数组在此区域分配。
堆内存逻辑分区:
新生代,new/young;老年代,odl/tenured,两者默认比例 new : old 为 1:3。
新生代,又划分为Eden区,suvivor0/from,suvivor1/to,三者比例默认为 8:1:1。
一个对象 new 出来后,首先尝试往 stack 上分配,stack 上分配不下,会进入 Eden区。一次GC垃圾回收幸存后进入 survivor0,再一次GC垃圾回收幸存后进入 survivor1,经过复制年龄限制后,才会进入 old 区。
为什么要给堆进行分代?
因为对象的生命周期不同,并且大部分对象是临时对象;给堆分代主要是为了优化GC性能。
3、Java 虚拟机栈
JVM stacks,每个线程对应一个栈,与线程同时创建,存储方法栈帧 frames。
栈帧 frame,存储的是:
local variables,局部变量表,方法内部使用的变量,方法参数也存储在此。
operand stacks, 操作数栈,每个局部变量都有自己的 operand stacks,通过指令查看。
dynamic linking,动态链接,常量池里的符号链接,方法名,类型等,看它是否已经解析,有的话直接用,没有的话,就进行动态解析。
return address,方法出口,比如方法 m 调用方法 n,n执行完之后,如果n有返回值,那么把返回值放到哪里,执行结束后应该回到哪个位置继续执行,这个位置叫return address。
4、本地方法栈
native method stacks,本地方法,C 和 C++方法。
5、直接内存
Direct Memory, jdk1.4版本之后增加的,为了提高IO效率,由用户空间直接去访问内核空间的内存,省略了IO拷贝到JVM的过程,实现了零拷贝 zero copy。
6、方法区
method area,线程间共享,存储每个类的结构信息 per-class structure。
Hotspot JVM JDK 1.8之前叫 perm space 永久区,字符串常量位于此区域,Full GC 不会清除此区域。JDK 1.8 之后叫 meta space 元数据区,使用本地内存来存储,如果没有指定这个参数,元空间会在运行时根据需要动态调整。不会再发生 java.lang.OutOfMemoryError: PermGen 异常。FGC会清理此区域,而字符串常量已被移动到堆中。