转载

JVM问题及解答

常见JVM问题

  1. JVM内存模型,GC机制和原理。 注意JVM内存模型与Java内存模型(JMM)不是同一个东西。 JVM = 类加载器(classloader) + 执行引擎(execution engine) + 运行时数据区域(runtime data area)
    JVM问题及解答
  • 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。
JVM问题及解答

参考: 多线程之Java内存模型(JMM)(一)

  1. 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的垃圾回收机制-垃圾收集算法(一)

  1. JVM里的有几种classloader,为什么会有多种? JDK中默认情况下有三种,如下图
    JVM问题及解答
  • BootStrap ClassLoader,负责加载<JAVA_HOME>/lib或被-Xbootclasspath指定路径下的类库,开发者不可以直接使用
  • Extension ClassLoader,负责加载<JAVA_HOME>/lib/ext或被java.ext.dirs系统变量指定的路径中的所有类库,开发者可以直接使用
  • App ClassLoader,这个类加载器是ClassLoader.getSystemClassLoader()的返回值,负责加载用户类路径上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义过类加载器,那么系统默认使用这个类加载器。

如果一个类收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类来实现,每一个层次的类加载器都是这样,因此所有的类加载请求都会最终传送到启动类加载器,只有当父类加载器无法完成这个加载请求,子类加载器才会自己尝试加载。

好处:避免内存中出现同样的字节码。可以很好的解决各个类加载器的基础类统一问题。

参考:JVM是如何加载类的?

  1. 什么情况下我们需要破坏双亲委派模型? 热替换,热部署。OSGI是Java业界广泛认可的模块化标准,而OSGI模块化热部署的关键是它自定义的类加载器。每一个模块都有一个自己的类加载器,当需要更换一个 Bundle(包) 时,Bundle连同类加载器一同替换实现代码热部署。

弄懂了OSGi的精髓,就可以算是掌握了类加载器的精髓

参考:JVM是如何加载类的?

  1. 常见的JVM调优方法有哪些?可以具体到调整哪个参数,调成什么值? 主要调节吞吐量和响应时间: 参考:记一次JVM调优 如何解决OutOfMemoryError

  2. JVM class文件结构是如何解析的;

  3. 可见性,重排序,final关键字 可见性是指一个线程修改某个共享变量的值的时候,其它线程能立刻得知。 重排序是底层做的优化,有些代码会在机器层面上提前执行,从而出现不正常的结果 final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。

参考:深入理解final关键字 8. Java对象模型 每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,对象头以及元数据。对象头中有一些运行时数据,其中就包括和多线程相关的锁的信息。元数据其实维护的是指针,指向的是对象所属的类的instanceKlass。

这个内容比较多,需要深入理解

参考:Java对象模型

  1. oop-klass

  2. 对象存活判定 可达性分析算法:可达性分析,通过一系列"GC Roots"的对象作为起始点,往下走,一个对象到GC roots没有任何引用的时候,则证明对象是不可用的 可作为GC roots的对象:

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI引用的对象
  1. Java的引用类型
  • 强引用:正常对象赋值的语句,String a = "abc";强引用可以直接访问目标对象,并且不会被系统回收,强引用可能导致内存泄漏
  • 软引用:比强引用稍微弱一点的类型,只有再内存不足的情况下才会被回收。如果再内存足够的时候,调用System.gc()也不会回收软引用的对象。
  • 弱引用:比软引用弱一点的引用类型,只要发现弱引用,都会将对象进行回收。
  • 虚引用:最弱的一个引用类型,一个持有虚引用的对象,和没有引用几乎是一样的。虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收的过程。

参考: Java Primitive type与References type

原文  https://juejin.im/post/5c660ba2f265da2dc849518d
正文到此结束
Loading...