《深入理解Java虚拟机:JVM高级特性与最佳实践(最新第二版)》
线程独享区域:程序计数器,本地方法栈,虚拟机栈
线程共享区域:元空间(<=1.7方法区), 堆
程序计数器:线程私有,是一块较小的内存空间,可以看做是当前线程执行的字节码指示器,也是唯一的没有定义OOM的区块
本地方法栈: 用于执行Native 方法时使用 虚拟机栈:用于存储局部变量,操作数栈,动态链接,方法出口等信息
元空间:存储已被虚拟机加载的类元信息,常量,静态变量,即时编译器编译后的代码等数据依旧存储在方法区中,方法区位于堆中
堆:存储对象实例
示例:
/**
* @author: jujun chen
* @description: 使用了CGLIB来动态生成类,元空间存储类信息,-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
* 如果只设置堆的大小,并不会溢出
* @date: 2019/4/7
*/
public class JavaMetaSpaceOOM {
static class OOMObject{}
public static void main(final String[] args) {
while (true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,objects);
}
});
enhancer.create();
}
}
}
//OOM -Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError
public class OOMTest {
public static void main(String[] args) {
List<Object> objList = new ArrayList();
while(true) {
objList.add(new Object());
}
}
}
//SOE栈异常 -Xss125k
public class SOETest() {
static int count = 0;
public static void main(String[] args) {
try {
stackMethod();
} catch(Error err) {
err.printStackTrace();
System.out.println("执行count=" + count);
}
}
private static void stackMethod() {
count ++;
stackMethod();
}
}
判断是否可以回收,或者存活主要是看:
多合一监视工具
更多资料请学习官网:https://docs.oracle.com/en/java/javase/11/tools/index.html
1. 响应时间优先:年轻代设的大些,直到接近系统的最低响应时间限制。年轻代设大,可以减少到达年老代的对象。对于永久代的设置需要参考:永久代并发收集的次数、年轻代和永久代回收时间比例,调整达到一个合适的值
2. 吞吐量优先:年轻代设的大些,永久代较小
1. Bootstrap ClassLoader(C++实现)
负责加载JDK自带的rt.jar包中的类文件,它是所有类加载器的父加载器,Bootstrap ClassLoader没有任何父类加载器。
2. Extension ClassLoader(ExtClassLoader)负责加载Java的扩展类库,也就是从jre/lib/ext目录下或者java.ext.dirs系统属性指定的目录下加载类。
3. System ClassLoader(AppClassLoader)负责从classpath环境变量中加载类文件,classpath环境变量通常由"-classpath" 或 "-cp" 命令行选项来定义,或是由 jar中 Mainfest文件的classpath属性指定,System ClassLoader是Extension ClassLoader的子加载器
4. 自定义加载器
一个类在加载的时候,首先会将加载请求委派给父加载器,只有当父加载器反馈无法加载完成这个请求时,子加载器才会尝试自己加载 双亲委派模型的破坏指的是不按照双亲委派模型来加载类,比如JNDI,它的代码由启动类加载器加载,但JDNI需要调用部署在ClassPath的JNDI接口,但启动类加载器是不知道这些代码的,所以就有了线程上下文类加载器(Thread Context ClassLoader),可以通过java.lang.Thread类setContextClassLoader设置类加载器,通过这个父加载器就可以请求子类加载器完成类加载的动作。
类的生命周期一个有7个阶段:加载、验证、准备、解析、初始化、使用、卸载
编译器优化分编译期和运行期
YoungGC:
YoungGC并不是在现有的Eden区放满了就马上触发,G1会计算现有的Eden区回收大概要多久时间,如果回收时间远小于参数-XX:MaxGCPauseMills 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时间接近参数-XX:MaxGCPauseMills设定的值,就会触发Young GC
MixedGC
不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中的存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC
Full GC
停止系统程序,然后采用单线程进行标记、清理、和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程非常耗时。(Shenandoah已经优化成多线程收集,Shenandoah可以认为是G1的升级版本)
ZGC 是JDK11 中加入的低延迟垃圾收集器,没有采用分代算法,清理过程大致分为:并发标记、并发预备重分配、并发重分配、并发重映射
坚持专研Java,一条路走到黑。持续更新地址 语雀:https://www.yuque.com/itsaysay/mzsmvg GitHub: https://github.com/jujunchen/Java-interview-question