运行时数据区域包括程序计数器,java虚拟机栈,本地方法栈,方法区和堆。其中,java虚拟机栈,本地方法栈,程序计数器是每个线程私有的。
程序计数器
可以看作是当前线程执行到的字节码的行号指示器。对于Java的多线程,为了使程序每次切换后能够恢复到正确的执行位置,因此每一个线程必须要有自己独立的程序计数器。如果线程执行的是Java方法,记录的是正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。
Java虚拟机栈
线程私有。线程每执行一个方法,都会在该线程的虚拟机栈中,创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息。方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
局部变量表 存放了各种基本数据类型,对象引用和ReturnAdress(你需要返回的字节码指令的地址)。
两种异常情况:
- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:
java -Xss512M HackTheJava
本地方法栈
跟虚拟机栈差不多,只不过它是为Native Method服务的。
一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。
Native Method详解:https://blog.csdn.net/wike163/article/details/6635321
https://www.cnblogs.com/HDK2016/p/7226840.html
堆
存放对象实例。所有线程共享。
OOM异常
方法区
用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在HotSpot中,也叫方法区为永久代。因为GC分代收集拓展到方法区。方法区主要是废弃类和常量的收集,对于方法区,也可以选择不进行垃圾回收。
OOM异常。
运行时常量池
运行时常量池是方法区的一部分。
Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。
运行时常量池具有动态性,除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。
String.intern()
如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。
详解:https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html