常见JVM问题
- JVM内存模型,GC机制和原理。 注意JVM内存模型与Java内存模型(JMM)不是同一个东西。 JVM = 类加载器(classloader) + 执行引擎(execution engine) + 运行时数据区域(runtime data area)
- PC寄存器:保存JVM正在执行的字节码指令地址。如果是native的,那么pc寄存器的值为undefined
- JVM Stack:存储局部变量与一些过程结果的地方。在方法调用和返回中也扮演了很重要的角色。
- Heap:堆是可以可供各个线程共享的运行时存储区域,也是供所有类的实例和数组对象分配内存的区域。堆在JVM启动的时候创建。堆所存储的就是被GC所管理的各种对象。
- Method Area:各个线程共享的运行时内存区,它存储每一个类的实例信息,运行时常量池,字段和方法数据,构造函数和普通方法的字节码等内容。还有一些特殊方法。
- Run-Time Constant Pool:在方法区中分配,在加载类和接口到虚拟机之后,就创建对应的运行时常量池。
- Native Method Stacks:JDK中native的方法,System类和Thread类中有很多。使用C语言编写的方法,这个也通常叫做C stack。
参考: 多线程之Java内存模型(JMM)(一)
- GC分哪两种,Minor GC 和Full GC有什么区别?什么时候会触发Full GC?分别采用什么算法?
- 从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。当Eden区满时,触发Minor GC。
- Full GC 是清理整个堆空间—包括年轻代和老年代。Major GC通常是跟full GC是等价的,收集整个GC堆。
- 调用System.gc时,系统建议执行Full GC,但是不必然执行,
- 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
- 老年代空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
Minor GC采用复制算法,将Eden区的对象复制到Suvivor空间,然后进行清除 Full Gc采用的是标记-压缩算法,让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。
参考: Java的垃圾回收机制-垃圾收集算法(一)
- JVM里的有几种classloader,为什么会有多种? JDK中默认情况下有三种,如下图
- BootStrap ClassLoader,负责加载<JAVA_HOME>/lib或被-Xbootclasspath指定路径下的类库,开发者不可以直接使用
- Extension ClassLoader,负责加载<JAVA_HOME>/lib/ext或被java.ext.dirs系统变量指定的路径中的所有类库,开发者可以直接使用
- App ClassLoader,这个类加载器是ClassLoader.getSystemClassLoader()的返回值,负责加载用户类路径上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义过类加载器,那么系统默认使用这个类加载器。
如果一个类收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类来实现,每一个层次的类加载器都是这样,因此所有的类加载请求都会最终传送到启动类加载器,只有当父类加载器无法完成这个加载请求,子类加载器才会自己尝试加载。
好处:避免内存中出现同样的字节码。可以很好的解决各个类加载器的基础类统一问题。
参考:JVM是如何加载类的?
- 什么情况下我们需要破坏双亲委派模型? 热替换,热部署。OSGI是Java业界广泛认可的模块化标准,而OSGI模块化热部署的关键是它自定义的类加载器。每一个模块都有一个自己的类加载器,当需要更换一个 Bundle(包) 时,Bundle连同类加载器一同替换实现代码热部署。
弄懂了OSGi的精髓,就可以算是掌握了类加载器的精髓
参考:JVM是如何加载类的?
-
常见的JVM调优方法有哪些?可以具体到调整哪个参数,调成什么值? 主要调节吞吐量和响应时间: 参考:记一次JVM调优 如何解决OutOfMemoryError
-
JVM class文件结构是如何解析的;
-
可见性,重排序,final关键字 可见性是指一个线程修改某个共享变量的值的时候,其它线程能立刻得知。 重排序是底层做的优化,有些代码会在机器层面上提前执行,从而出现不正常的结果 final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。
参考:深入理解final关键字 8. Java对象模型 每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,对象头以及元数据。对象头中有一些运行时数据,其中就包括和多线程相关的锁的信息。元数据其实维护的是指针,指向的是对象所属的类的instanceKlass。
这个内容比较多,需要深入理解
参考:Java对象模型
-
oop-klass
-
对象存活判定 可达性分析算法:可达性分析,通过一系列"GC Roots"的对象作为起始点,往下走,一个对象到GC roots没有任何引用的时候,则证明对象是不可用的 可作为GC roots的对象:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- Java的引用类型
- 强引用:正常对象赋值的语句,String a = "abc";强引用可以直接访问目标对象,并且不会被系统回收,强引用可能导致内存泄漏
- 软引用:比强引用稍微弱一点的类型,只有再内存不足的情况下才会被回收。如果再内存足够的时候,调用System.gc()也不会回收软引用的对象。
- 弱引用:比软引用弱一点的引用类型,只要发现弱引用,都会将对象进行回收。
- 虚引用:最弱的一个引用类型,一个持有虚引用的对象,和没有引用几乎是一样的。虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收的过程。
参考: Java Primitive type与References type
原文
https://juejin.im/post/5c660ba2f265da2dc849518d