运行时数据区内容
JVM运行时数据区的内容包括:JAVA堆、方法区、虚拟机栈、本地方法栈、程序计数器。
JAVA堆:被所有线程共享的一块内存区域,用于存放对象实例,所有的对象实例和数组都要在堆上分配,JAVA堆的大小可以通过-Xmx和-Xms控制。
方法区:也是所有线程共享的一块内存区域,存储类信息、常量、静态变量、JIT编译后的代码。HOTSPOT虚拟机把GC分代扩展到方法区,使之可以像JAVA堆一样管理内存,方法区的回收目标是常量池的回收和对类型的卸载。
运行时常量池:它是方法区的一部分,CLASS文件中有部分是常量池,用于各种常量和符号引用,类加载后存入方法区的运行时常量池。运行时常量池相对于CLASS文件的常量池的一个重要特性是,并非置入CLASS文件常量池的内容才进入运行时常量池,运行期间也有可能新的常量被放入池中。
虚拟机栈:线程私有,描述的是JAVA方法的内存模型,每个方法执行的同时会创建一个栈帧,存放局部变量表、操作数栈、等。每个方法从调用到完成,对应一个栈帧的入栈到出栈。
局部变量表存放各种基本数据类型和对象引用。
本地方法栈:和虚拟机栈类似,只不过本地方法栈使用的是native服务。
程序计数器:当前线程所执行字节码的行号指示器,字节码解释器工作是通过改变这个计数器的值来读取下一条所需执行字节码指令
对象的创建
- new指令,检查能否在常量池定位到一个类的符号引用,检查这个符号引用对应的类是否已被加载,如果没有,执行类加载过程。
- 类加载完成后,为对象分配内存,内存大小在类加载后就确定了,java堆上分配内存,使用“指针碰撞”的方式,由于划分对象空间十分频繁,所以为了解决并发引起的线程安全问题,有两种解决方案:1,对分配内存空间进行同步处理;2、把分配内存的操作划分在不同空间,每个线程在java堆预先分配一快内存,称为本地线程分配缓冲(TLAB),哪个线程分配内存,就在哪个线程的TLAB上进行。
- 分配内存完成后,对所分配的内存空间初始化为0,保证对象的实例字段不辅初值就可以使用。
- 对对象进行设置,如是哪个类的实例,hash,GC年龄。
对象的访问定位
对象访问方式取决于虚拟机,
句柄访问:reference指向句柄池,句柄池中存放对象实例数据的指针和对象类型数据的指针。
直接指针访问:reference指向对象实例数据,对象实例数据里的对象类型指针指向对象类型数据。
OOM异常
java堆OOM:不断的创建对象,就会溢出。
方法区OOM:常量池中存放大量数据,如string.intern(),加载大量首次出现的字面量,就会造成常量池数据溢出。
虚拟机栈OOM:线程请求的栈深度大雨虚拟机允许的最大深度,SOF;虚拟机在扩展栈时无法申请更大内存,OOM。
SOF情况:定义大量本地变量;栈深度过大(递归)
JVM参数
方法区:-XX:PermSize和-XX:MaxPermSize
虚拟机栈:-Xss
JAVA堆:-Xms和-Xmx
直接内存:-XX:MaxDirectMemorySize