1. 谈谈你对JVM的理解?Java 8虚拟机和之前的变化更新?
JVM(Java Virtual Machine)的理解: JVM是执行Java字节码的引擎,它将Java程序转换为机器指令进行执行。JVM本质上是跨平台的,它通过将Java编译成字节码(.class文件)来实现一次编写,处处运行。JVM的主要职责包括:
- 类加载:通过类加载器将字节码加载到内存中。
- 字节码执行:通过解释器或JIT(即时编译器)将字节码转换为本地机器码执行。
- 内存管理:JVM管理运行时内存区域,主要包括堆(Heap)、方法区(Method Area)、栈(Stack)等区域。
- 垃圾回收(GC) :JVM负责清理无用对象,通过垃圾回收机制提高内存使用效率。
Java 8虚拟机与之前版本的变化:
-
永久代(PermGen)被元空间(Metaspace)取代:
在Java 7及之前,方法区(Method Area)是使用永久代(PermGen)来存储类的元数据、常量池、静态变量等。永久代的内存大小是有限的,容易引发OutOfMemoryError: PermGen space。
在Java 8中,永久代被移除,取而代之的是元空间(Metaspace),它直接使用本地内存来存储类的元数据信息,因此不再需要手动设置永久代的大小,减少了OOM的几率。 -
垃圾回收器(GC)优化:
- Java 8默认的GC是Parallel GC,适合吞吐量优先的场景。
- 引入了G1 GC(Garbage-First Garbage Collector),它是面向延迟敏感应用的垃圾回收器,适合大内存应用,并且提供了更好的暂停时间控制。
-
Lambda表达式和函数式接口:Java 8引入了Lambda表达式以及函数式编程的支持,使代码更加简洁和灵活。这是编译时的改进,JVM的运行也能更好地适应这些变化。
-
默认方法:接口可以包含默认实现的方法,这个特性虽然属于语法层面,但JVM也需要适应这些新特性的处理。
2. 什么是OOM(OutOfMemoryError),什么是栈溢出StackOverFlowError?怎么分析?
OutOfMemoryError(OOM) : 这是当JVM在运行时无法为对象分配足够的内存时抛出的错误。OOM常见的几种情况:
- Java Heap Space OOM:堆内存不足,无法创建新的对象。通常是因为大量对象持久驻留在内存中,无法被垃圾回收器清理。
- Metaspace OOM:元空间溢出,通常是由于类的加载过多(例如动态生成类)。
- Direct Buffer OOM:直接内存(Direct Memory)不足,多出现在NIO操作中,使用直接缓冲区时。
分析方法:
-
使用
jmap命令生成Heap Dump文件:lua 复制代码 jmap -dump:live,format=b,file=dump.hprof <pid>然后可以使用MAT(Memory Analyzer Tool)分析
dump.hprof文件,找出大量占用内存的对象。 -
检查GC日志,使用参数
-XX:+PrintGCDetails查看GC的详细情况,是否频繁发生Full GC。
StackOverflowError: 这是由于方法调用栈太深,导致栈空间耗尽而抛出的错误,通常由递归调用没有退出条件或者深层次的嵌套调用引起。
分析方法:
-
检查代码中的递归调用,确保有合理的退出条件。
-
通过设置栈的大小限制,防止栈溢出:
diff 复制代码 -Xss512k调整该参数可以限制每个线程的栈大小。
3. JVM的常用调优参数有哪些?
以下是一些常用的JVM调优参数:
-
堆内存设置:
-Xms:设置JVM启动时的堆内存大小(初始大小)。-Xmx:设置JVM最大可用的堆内存大小。
diff 复制代码 -Xms512m -Xmx1024m -
垃圾回收器参数:
-XX:+UseG1GC:启用G1垃圾回收器,适用于大内存、低延迟的应用场景。-XX:+UseConcMarkSweepGC:启用CMS垃圾回收器,适用于低停顿需求的应用场景。-XX:MaxGCPauseMillis:设置GC停顿时间的目标上限。-XX:+PrintGCDetails:输出GC的详细日志。
-
栈大小设置:
-Xss:设置每个线程的栈大小,默认是1MB,可以调整以防止StackOverflowError。
diff 复制代码 -Xss512k -
元空间设置:
-XX:MetaspaceSize:设置元空间初始大小。-XX:MaxMetaspaceSize:设置元空间最大大小。
-
直接内存设置:
-XX:MaxDirectMemorySize:限制直接内存的大小,特别在使用NIO或Netty时需要考虑。
4. 内存快照如何抓取,怎么分析Dump文件?
抓取内存快照:
-
使用
jmap工具可以生成JVM的内存快照(Heap Dump):lua 复制代码 jmap -dump:format=b,file=heapdump.hprof <pid>或者在OOM时通过
-XX:+HeapDumpOnOutOfMemoryError自动生成dump文件。
分析Dump文件:
-
使用MAT(Memory Analyzer Tool)分析Dump文件:
- 导入Dump文件后,可以查看哪些对象占用了大量内存。
- 可以使用MAT提供的“泄漏可疑报告”找到可能的内存泄漏点。
- 查看GC Roots,判断对象的存活时间及其依赖链条。
-
另外,VisualVM工具也可以用于分析内存泄漏、线程状态等信息。它提供了更加图形化的界面来查看JVM内存和线程的使用情况。
5. 谈谈JVM中类加载器的认识
类加载器(ClassLoader)负责将Java类加载到JVM内存中,采用了双亲委派模型。
-
双亲委派模型: 当一个类加载器收到类加载请求时,会先把请求委派给父类加载器,逐层向上委派,直到最顶层的根类加载器(Bootstrap ClassLoader)。如果父类加载器找不到该类,子类加载器才会尝试自己加载。
-
常见的类加载器:
- Bootstrap ClassLoader:负责加载JVM核心类库(如
rt.jar)。 - Extension ClassLoader:负责加载扩展类库(如
/lib/ext目录下的类)。 - Application ClassLoader:负责加载应用程序的类(如项目中的
classpath下的类)。
- Bootstrap ClassLoader:负责加载JVM核心类库(如
-
自定义类加载器: 可以继承
ClassLoader实现自定义类加载器,用于特殊场景,例如动态加载远程类、模块化系统中的类隔离等。需要重写findClass()方法来指定如何查找和加载类。 -
类加载器的重要性:
- 类加载器不仅负责类的加载,还涉及类的隔离和命名空间。例如在Tomcat等应用服务器中,不同的Web应用可以使用不同的类加载器,避免类冲突。
- 双亲委派模型确保了核心类不会被自定义的类加载器篡改,保证了Java安全模型的可靠性。