- 1. 如何对”.class”文件处理保证不被人拿到以后反编译获取公司核心源码?
- 2. Tomcat这种Web容器中的类加载器应该如何设计实现?
- 3. 我们创建的那些对象,到底在Java堆内存里会占用多少内存空间呢?
- 4. 加载到方法区的类会被垃圾回收吗?什么时候被回收?
- 5. 每个线程都有Java虚拟机栈,里面也有方法的局部变量等数据,这个Java虚拟机栈需要进行垃圾回收吗?为什么?
- 6. 分析一下自己手头负责的那些业务系统,哪些是短生存周期的对象,哪些是长生存周期的对象?
- 7. Tomcat、Spring Boot部署启动系统的时候,JVM参数如何设置?
- 8. 思考自己平时负责的系统,有没有按照这个思路去预估业务系统压力,然后给一个合理的内存设置?
- 9. 以下代码,如果垃圾回收,会回收 ReplicaFetcher 对象吗?
- 10. 新生代垃圾回收,万一存活下来的对象超过了10%的内存空间,在另一块Survivor区中放不下怎么办?
- 11. 万一我们突然分配了一个超级大的对象,新生代找不到连续内存空间存放,怎么办?
- 12. 什么时候会触发 Minor GC?
- 13. 触发 Minor GC 之前会如何检查老年代大小,涉及那几个步骤和条件?
- 14. 什么时候在 Minor GC 之前就会提前触发一次 Full GC?
- 15. Full GC 的算法是什么?
- 16. Minor GC 过后可能对应哪几种情况?
- 17. 那些情况下 Minor GC 后的对象会进入老年代?
- 18. 到底是单线程进程垃圾回收好呢?还是多线程进行垃圾回收好呢?在不同的场景下有各自的优缺点吗?
- 19. parnew+cms的gc,如何保证只做ygc,jvm参数如何配置?
- 20. 为什么老年代的垃圾回收速度会比新生代的垃圾回收速度慢很多倍?到底慢在哪里?
- 21. CMS垃圾回收的运行机制
- 22. 说几个触发老年代GC的时机?
- 23. 从新生代的垃圾回收来看,大家觉得G1垃圾回收器在新生代垃圾回收过程中,相比之前的ParNew而言,最大的进步在哪里?
- 24. G1垃圾回收器在什么场景下适用?有了 G1 后,是不是还有一些场景采用 ParNew+CMS 垃圾回收器也可以?
1. 如何对”.class”文件处理保证不被人拿到以后反编译获取公司核心源码?
既然”.java”可以编译成”.class”,那么”.class”也就可以反编译成”.java”,这样核心源码就可以被阅读。
根据类加载的过程,及实现这个过程的类加载器的相关知识,提出办法:
- 首先编译的时候,使用一些小工具对 .class 字节码加密,或混淆等处理;
- 有很多商业级产品,可以付费给字节码文件加密
- 在类加载的时候,对加密的类,使用自定义的类加载器来解密文件即可。
2. Tomcat这种Web容器中的类加载器应该如何设计实现?
Tomcat 本身就是用 Java 写的,他自己就是一个 JVM,那么,Tomcat 的类加载机制应该怎么设计,才能把我们动态部署进去的 war 包中的类,加载到 Tomcat 自身运行的 JVM 中,然后去执行那些我们写好的代码呢?
首先,Tomcat 自定义了很多如 Common、Catalina、Shared 等类加载器,用来加载 Tomcat 自己的一些核心基础类库;
然后,Tomcat 为每个部署在里面的 web 应用都有一个对应的 WebApp 类加载器,负责加载我们部署的这个 Web 应用的类;
最后,Tomcat 是打破了双亲委派机制,每个 WebApp 负责加载自己对应的 Web 应用的 class 文件,不会去求爸爸帮忙加载,自己的事情自己做!
3. 我们创建的那些对象,到底在Java堆内存里会占用多少内存空间呢?
我们在Java堆内存中分配的那些对象,到底会占用多少内存?一般怎么来计算和估算我们的系统创建的对象对内存占用的一个压力呢?
这里只是给一个估算占用空间的思路:
- 一个对象实例占用的内存空间,大致分为两块:
- 一个是对象实例自己本身的一些信息
- 一个是对象实例中变量作为数据占用的空间
- 比如:
- 对象头,如果在64位的 linux 操作系统上,会占用16字节;
- 实例对象内部变量:
- int 类型的实例变量,它会占用4个字节;
- long 类型的实例变量,它会占用8个字节;
- 数组、Map 之类的,那么就会占用更多的内存了;
4. 加载到方法区的类会被垃圾回收吗?什么时候被回收?
- 方法区,即永久代中的类也会被垃圾回收;
- 满足以下三种个条件就可以回收方法区中的该类:
- 该类的所有实例对象都已经从 Java 堆内存里被回收;
- 加载这个类的 ClassLoader 已经被回收;
- 对该类的 Class 对象没有任何引用(Class clazz = replicaManager.getClass();)
5. 每个线程都有Java虚拟机栈,里面也有方法的局部变量等数据,这个Java虚拟机栈需要进行垃圾回收吗?为什么?
每个线程执行方法的时候,那些方法对应的栈帧出栈了,里面的局部变量直接就从内存里清理掉了,不会触发垃圾回收;
JVM 的垃圾回收针对的是新生代、老年代、方法区,不会针对方法的栈帧。
6. 分析一下自己手头负责的那些业务系统,哪些是短生存周期的对象,哪些是长生存周期的对象?
目的是家开始在脑子里建立起来自己负责的系统在 JVM 中运行时的一个概念图,要有这个意识,才能更好的进行 JVM 调优。
7. Tomcat、Spring Boot部署启动系统的时候,JVM参数如何设置?
Spring Boot 其实就是启动的时候可以加上 JVM 参数,Tomcat 就是在 bin 目录下的 catalina.sh 中可以加入 JVM 参数。
8. 思考自己平时负责的系统,有没有按照这个思路去预估业务系统压力,然后给一个合理的内存设置?
其实就是希望大家以后建立起来一个全面的工程素养,每个合格的工程师,都应该在上线系统的时候,对系统压力做出预估,然后对 JVM 内存、磁盘空间大小、网络带宽、数据库压力做出预估,然后各方面都给出合理的配置。
9. 以下代码,如果垃圾回收,会回收 ReplicaFetcher 对象吗?
public class Kafka{
public static ReplicaManager replicaManager = new ReplicaManager();
}
public class ReplicaManager {
public ReplicaFetcher replicaFether = new ReplicaFetcher();
}
是不会的,因为 ReplicaFetcher 对象被 ReplicaManager 对象中的实例变量引用了,然后 ReplicaManager 对象被 Kafka 类的静态变量给引用了;
所以垃圾回收的时候,是不会回收掉 ReplciaFetcher 对象的,否则让存活下来的 ReplicaManager 对象情何以堪?
10. 新生代垃圾回收,万一存活下来的对象超过了10%的内存空间,在另一块Survivor区中放不下怎么办?
转移到老年代,如果老年代放不下,触发 Full GC,如果老年代还放不下,导致 OOM 内存溢出。
11. 万一我们突然分配了一个超级大的对象,新生代找不到连续内存空间存放,怎么办?
直接放入到老年代;
12. 什么时候会触发 Minor GC?
Eden区和存对象的Survivor区满的时候触发MinorGC
13. 触发 Minor GC 之前会如何检查老年代大小,涉及那几个步骤和条件?
检查新生对所有对象所占空间是否大于老年代可用空间, 如果小于,进行MinorGC。
如果大于,查看”-XX:-HandlePromotionFailure”是否设置。
如果没设置,进行FullGC。如果设置如果没设置,进行FullGC。
如果设置,判断老年代空间是否大于之前MinorGC后进入老年代对象的平均大小。 如果大于,进行MinorGC。 如果
小于,进行FullGC。
14. 什么时候在 Minor GC 之前就会提前触发一次 Full GC?
(1) 新生代对象大小大于老年代空间,且没有设置”-XX:-HandlePromotionFailure”
(2) 设置了”-XX:-HandlePromotionFailure”,老年代可用空间小于之前每次MinorGC后进入老年代的平均大小
15. Full GC 的算法是什么?
标记整理算法,速度很慢;
16. Minor GC 过后可能对应哪几种情况?
(1)小于Survivor区域,进入Survivor区域
(2)大于survivor区域,小于老年代可用空间,进入老年代
(3)大于survivor区域,大于老年代可用空间,进行FullGC,如果FullGC后,老年代可用空间仍小于存活对象,抛出OOM
17. 那些情况下 Minor GC 后的对象会进入老年代?
(1) 经过15次(默认,可以设置)MinorGC的
(2) 某个年龄的对象大于survivor区域的
18. 到底是单线程进程垃圾回收好呢?还是多线程进行垃圾回收好呢?在不同的场景下有各自的优缺点吗?
- 启动系统的时候是可以区分服务器模式和客户端模式的,如果你启动系统的时候加入“-server”就是服务器模式,如果加入“-cilent”就是客户端模式。
- 服务器模式一般运行大型系统,通常都是多核 CPU,多线程垃圾回收器更能充分发挥性能;
- 客户端模式现在很少了,例如,应用的 Windows 客户端,运行在单核操作系统下,此时使用 单线程垃圾回收器反而比多线程的更高效些,因为不用频繁的切换,增加额外开销;
19. parnew+cms的gc,如何保证只做ygc,jvm参数如何配置?
结合自己系统的运行时内存占用情况,GC 后的对象存活情况,合理分配 Eden、Survivor、老年代的内存大小,合理设置一些参数,即可做到;
20. 为什么老年代的垃圾回收速度会比新生代的垃圾回收速度慢很多倍?到底慢在哪里?
从标记对象、清理对象的过程分析:
- 新生代:
- 首先,新生代里的对象的生命周期一般极短,很多都是没有被引用的垃圾对象,因此 GC Roots 追踪时直接从这些方法的局部变量、类的静态变量 GC Roots 出发查看引用到哪些对象,这些对象就是存活对象,速度极快;
- 其次,新生代清理垃圾对象时,将存活对象复制到 Suvivor,一次性直接回收 Eden 和之前使用的 Survivor,不需要内存碎片整理;
- 老年代:
- 两个最耗时的阶段:
- 并发标记阶段需要对老年代所有的对象进行 GC Roots 追踪,这个过程很慢;
- 并发清理阶段会产生大量内存碎片,GC 之后需要进行内存碎片整理;
- 碎片整理阶段会进入 Stop the World,系统会更慢;
- 并发清理阶段,如果老年代预留的空间不足以放入即将转入老年代的对象,会引发 Concurrent Mode Failure 问题,它会用 Serial Old 垃圾回收器,进入 Stop the World 后重新来一遍回收的过程,更耗时。
- 两个最耗时的阶段:
21. CMS垃圾回收的运行机制
- 为了避免长时间“Stop the World”,CMS采用了4个阶段来垃圾回收,其中初始标记和重新标记,耗时很短,虽然会导致“Stop the World”,但是影响不大。
- 然后并发标记和并发清理,两个阶段耗时最长,但是是可以跟系统的工作线程并发运行的,所以对系统没太大影响。
- 这就是 CMS 的基本工作原理。
22. 说几个触发老年代GC的时机?
- 是老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开;
- 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC;
- 是新生代Minor GC后的存活对象大于Survivor,那么就会进入老年代,此时老年代内存不足。
- 使用“-XX:CMSInitiatingOccupancyFaction”参数,老年代的内存占用达到一定比例,1.6 JDK 默认是 92%,触发 Full GC;
上述情况都会导致老年代 Full GC。
23. 从新生代的垃圾回收来看,大家觉得G1垃圾回收器在新生代垃圾回收过程中,相比之前的ParNew而言,最大的进步在哪里?
24. G1垃圾回收器在什么场景下适用?有了 G1 后,是不是还有一些场景采用 ParNew+CMS 垃圾回收器也可以?
1、G1压缩内存空间会比较有优势,适合会产生大量碎片的应用;
2、G1能够可预期的GC停顿时间,对高并发应用更有优势
3、其他垃圾收集器对大内存回收耗时较长,G1对内存分成多块区域,能够根据预期停顿时间选择性的对垃圾多的区
域进行回收,适用多核、jvm内存占用大的应用
4、parNew+cms回收器比较适用内存小,对象能够在新生代中存活周期短的应用