线程共享区域
1.java堆 几乎所有的对象实例以及数组都应当在堆上分配。
2.方法区(非堆) 存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。(由永久代迁移到元空间)
补:元空间(Metaspace)
这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收有时又确实是必要的。根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
3.运行时常量池 是方法区的一部分用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
4.直接内存 不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。NIO可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
对象的创建
1.检查是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、2.解析和初始化过。如果没有,那必须先执行相应的类加载过程。
3.为对象分配内存,并给分配到的内存空间初始化0。保证对象的实例字段不赋初始值就可以直接使用。
3.为对象进行一些设置(比如对象属于哪个类的实例)
4.调用构造函数进行初始化
对象的内存布局
对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
对象头
1.用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。
2.对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针。
对象的访问定位
Java程序会通过栈上的reference数据来操作堆上的具体对象。
堆溢出
-Xms 初始堆
-Xmx 最大堆
-Xmn 新生代
栈溢出 线程请求的栈深度大于虚拟机允许的深度,会抛出StackOverflowError。(允许动态扩充,当扩充栈容量无法申请到内存会发生OutOfMemoryError)
-Xss
常量池及方法区(常量池属于方法区,JDK7 以后永久代中的常量池移入java堆中,方法区移入Metaspace中)
-XX:MetaspaceSize(上下浮动,回收后剩余空间小会增大,剩余空间大会减小)
-XX:MaxMetaspaceSize
直接内存
-XX:MaxDirectMemorySize