线程共享:
堆、方法区(运行时常量池)、直接内存
用来记录程序正在执行的字节码指令的地址
异常:
当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,本地方法栈为本地方法服务。
异常:
OutOfMemoryError 异常
异常:
OutOfMemoryError 异常
从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中。
常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References)。
符号引用
属于编译原理方面的概念,包括了如下三种类型的常量:
(1)类和接口的全限定名;
(2)字段名称和描述符;
(3)方法名称和描述符。
在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。
主要针对堆(新生代、老年代)和方法区(永久代)
4种引用:
(1)强引用
(2)软引用(内存不足时才会回收)
(3)弱引用(下一次垃圾回收时会被回收)
(4)虚引用(为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知。)
分代收集
新生代:复制
老年代:标记清除或者标记整理
(1)Serial?
串行、新生代、客户端(默认客户端新生代的收集器)、标记整理
(2)ParNew
并行、新生代、服务端(Serial的并行版),只能与CMS配合使用、标记整理
(3)Parallel Scavenge
并行、新生代、吞吐量优先(降低了并发数,其他收集器是降低用户线程的停顿时间,提升并发数;减少新生代空间——>垃圾回收频繁——>吞吐量下降,缩短吞吐量牺牲了吞吐量,减少了新生代空间)
(4)Serial Old
串行、老年代、客户端
(5)Parallel Old
并行、老年代、(服务端后台)
(6)CMS(Concurrent Mark Sweep并发标记清除)
并行、老年代、服务端
低停顿
过程:初始标记(需要停顿)、并发标记、重新标记(解决并发标记期间发生变化的标记,需要停顿)、并发清除(期间发生引用变化会导致浮动垃圾,只能下一次GC再清理)
缺点:吞吐量低、无法处理浮动垃圾需要临时使用Serial Old处理、标记清除算法会导致不连续空间的浪费
(7)G1
服务端
引入Region
过程:初识标记、并发标记、最终标记、筛选回收
内存分配策略:
(1)对象优先分配在Eden
(2)大对象直接进入老年代(-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配)
(3)长期存活的对象进入老年代(-XX:MaxTenuringThreshold 用来定义年龄的阈值)
(4)动态年龄判断(如果Survivor相同年龄的对象大于Survivor空间的一半,大于等于该年龄的直接进入老年代)
(5)空间分配担保
MinorGC之前,老年代最大可用连续空间大于新生代所有对象大小,则安全,否则不安全,出发空间分配担保的检查。
如果允许担保失败,则判断老年代的连续空间大于之前晋升到老年代对象的平均大小,如果大于则进行尝试MinorGC;
否则,担保失败,或者老年代空间较小,都会需要先进行一次FullGC
Concurrent Mode Failure(CMS导致的老年代空间不足)
public static int value1 = 123; //初始化为0
public static final int value2 = 123; //初始化为123
(4)解析
将常量池的符号引用替换为直接引用的过程。
(5)初始化
使用用户设置的值初始化,(上面的代码块中)即将123赋值给value1的过程。
初始化阶段是虚拟机执行类构造器
初始化顺序:
public class Test {
static {
i = 0; // 给变量赋值可以正常编译通过
System.out.print(i); // 这句编译器会提示“非法向前引用”
}
static int i = 1;
}
父接口中的变量,只有在子接口中使用时才会初始化。
System.out.println(SubClass.value); // value 字段在 SuperClass 中定义
(2)数组定义引用类,不会触发类初始化,只会触发数组类的初始化,数组类继承自Object
SuperClass[] sca = new SuperClass[10];
(3)使用常量不会触发类初始化
System.out.println(ConstClass.HELLOWORLD);
类加载检查——>内存分配——>初始化零值——>设置对象头——>执行init方法
执行init方法
按照程序猿设置的值初始化。
对齐填充:仅仅占位,保证整个对象对齐,8字节的整数倍。
直接指针(堆):到对象类型数据的指针(指向对象类型数据(方法区))、对象实例数据(堆)
好处:少了一次指针定位的时间开销。
原文:https://www.cnblogs.com/suyeSean/p/11241906.html