Java面试01|JVM相关

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试01|JVM相关相关的知识,希望对你有一定的参考价值。

 

1、JVM内存查看与分析,编写内存泄露实例 

 

堆区、栈区、方法区、本机内存都有可能内存溢出。在这里编写堆区内存溢出实例。如下(来自《深入理解Java虚拟机》一书。

// -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
public class HeapOutOfMemoryError {
	static class OOMObject{}
	public static void main(String[] args) {
		List<OOMObject> list = new ArrayList<OOMObject>();
		while(true){
			list.add(new OOMObject());
		}
	}
}

编写溢出实例主要知道两点内容:

(1)不断创建实例对象,以占满堆空间

(2)保证GC Roots到对象之间有可达路径来避免垃圾回收

最后运行的结果如下截图。

技术分享

Dump出快照后查看泄露对象和泄露对象到GC Roots的引用链。安装Eclipse Memory Analyzer到Eclipse IDE进行内存快照查看。

插件安装地址:http://www.eclipse.org/mat/downloads.php

 

使用jstat命令来查看JVM内存的分配与使用情况,如下:

jstat -gc 24170(进程号) 1000(第1000ms采集一次数据) 2(采集两次数据)

结果如下:

技术分享


S0C:S0区容量(S1区相同)   S0U:S0区已使用
EC:  E区容量           EU:E区已使用
OC: 老年代容量          OU:老年代已使用
MC: 元数据容量          MU: 元数据区已使用
YGC:  Young GC(Minor GC)次数   YGCT:Young GC总耗时   FGC:  Full GC次数  FGCT:Full GC总耗时  GCT: GC总耗时

也可以使用Java VisualVM图形界面进行查看。一般可以装个Visual GC插件,如下所示。

技术分享

介绍一下如上截图的参数。 

1、Compile Time(编译时间):855compiles 表示编译总数,6.606s表示编译累计时间。一个脉冲表示一次JIT编译,窄脉冲表示持续时间短,宽脉冲表示持续时间长。

2、Class Loader Time(类加载时间): 20869loaded表示加载类数量, 139 unloaded表示卸载的类数量,40.630s表示类加载花费的时间

3、GC Time(GC Time):2392collections表示垃圾收集的总次数,37.454s表示垃圾收集花费的时间,last cause表示最近垃圾收集的原因

4、Eden Space(Eden 区):括号内的31.500M表示最大容量,9.750M表示当前容量,后面的4.362M表示当前使用情况,2313collections表示垃圾收集次数,8.458s表示垃圾收集花费时间

5、Survivor 0/Survivor 1(S0和S1区):括号内的3.938M表示最大容量,1.188M表示当前容量,之后的值是当前使用情况

6、Old Gen(老年代):括号内的170.500M表示最大容量,67.500M表示当前容量,之后的31.825表示当前使用情况,3collections表示垃圾收集次数 ,450.789s表示垃圾收集花费时间

7、Perm Gen(永久代):括号内的96.000M表示最大容量,70.250M表示当前容量,之后的35.129M表示当前使用情况

   

2、对象的分配以及出发Minor GC与Full GC的条件

对象优先在Eden分配,当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。

在发生Minor GC之前,JDK7规则是只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,会发生Minor GC,否则会发生Full GC。

  

3、类加载过程,如何获得当前对象的ClassLoader

类加载的过程分为:加载、连接、初始化、使用和卸载。其中连接又可以分为:验证、准备和解析

获取当前对象的ClassLoader通过如下代码:

this.getClass().getClassLoader()  // 获取当前对象的类对象,然后调用getClassLoader

类加载器可以进行类层次的划分、OSGi、热部署和代码加密等。那么用户如何自定义类加载器呢?

要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用。

findClass protected Class<?> findClass(String name)   throws ClassNotFoundException

使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。默认实现抛出一个 ClassNotFoundException。

 

 

4、Java的先行发生关系 happens-before

JMM(Java内存模型)为所有程序内部动作定义了一个偏序关系,叫做happens-before。要想保证执行动作B的线程看到动作A的结果(无论A和B是否发生在同一个线程中),A和B之间就必须满足happens-before关系。

参考《深入理解Java虚拟机》376页

 

 

 

 

5、JVM的类载入器

(1)每个类加载器都加载哪些类

(2)如何自定义自己的类加载器,自己的类加载器和Java自带的类加载器关系如何处理

不同类加载器的命名空间关系:

同一个命名空间内的类是相互可见的。

子加载器的命名空间包含所有父加载器的命名空间。因此子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。

由父加载器加载的类不能看见子加载器加载的类。

如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。

 

参考:

《Java程序员修炼之道》第110页,看依赖注入中的类加载器

 

 

6、垃圾回收策略

 

技术分享

 具体说一下G1的回收策略:

 Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率 。

 

 

 

7、JVM参数

参数大体可以分为:堆设置、收集器设置和垃圾回收统计信息

 

参数名称 含义  
-Xms 初始堆大小,默认为物理内存的1/64(<1GB) 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx 最大堆大小,默认为物理内存的1/4(<1GB) 默认空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn 年轻代大小(1.4 or lator) 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。
整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.
增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)
 
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
 
-Xss 每个线程的堆栈大小 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。

-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:HeapDumpOnOutOfMemoryError
发生OOM时转储堆到文件,这是一个非常好的诊断方法。
 
-XX:+PrintGCDetails
打印GC详情
 
-XX:+UseParallelGC
设置并行收集器
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 











以上是关于Java面试01|JVM相关的主要内容,如果未能解决你的问题,请参考以下文章

java面试Java后端开发岗面试中JVM(java虚拟机)相关的常见问题

神奇!居然真的有阿里大牛用716页笔记只讲Java虚拟机及面试相关

JVM 面试题,安排上了!!!

Java相关面试题总结+答案

Java中JVM相关面试题-整理

(Java实习生)每日10道面试题打卡——JVM篇