目录
3、JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。
4、JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
6、JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作 内存等。
7、简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
1、什么情况下会发生堆、栈内存溢出。
栈溢出:方法执行时创建的栈帧超过了栈的深度 ,最有可能就是方法递归调用。
堆溢出:heap space表示堆空间,堆中主要存储的是对象。不断的new对象会导致堆中的空间溢出
2、JVM的内存结构,Eden和Survivor比例。
JVM区域总体分两类,heap区和非heap区。
heap区又分为:
Eden Space(伊甸园)、
Survivor Space(幸存者区)、
Old Gen(老年代)。
非heap区又分:
Code Cache(代码缓存区);
Perm Gen(永久代);(jdk1.8为元空间)
Jvm Stack(java虚拟机栈);
Local Method Statck(本地方法栈);
1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1
3、JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。
首先说如果没有Survivor区会出现什么情况:此时每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后会触发Major Gc(通常伴随着MinorGC,可以看做Full GC),比较耗时。
如果只有1个Survivor区,那当Eden区满了之后,就会复制对象到Survivor区,容易产生内存碎片化。严重影响性能。
所以使用2个Survivor区,始终保持有一个空的Survivor区,可以避免内存碎片化。
Survivor减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历多次Minor GC还能在新生代中存活的对象,才会被送到老年代。
4、JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
对象诞生即新生代->eden,使用标记-清除垃圾回收算法,在进行minor gc过程中,如果依旧存活,移动到Survivor,进行标记代数,这里有两块survivor内存,通过复制算法,从根节点遍历,然后将遍历标记的复制到另外一个空的survivor中,并且回收之前的survivor区数据,survivor区默认长到15代之后,晋升为老年代,在老年代使用CMS收集器,依次进行初始标记、并发标记、重新标记、并发清除。当老年代满了之后会触发Major Gc(通常伴随着MinorGC,可以看做Full GC)
5、讲下cms和G1,包括原理,流程,优缺点。
CMS:
一、初始标记:快速遍历gc roots节点
二、并发标记:跟踪gc roots
三、重新标记:标记那些并发进程中未被标记的
四、并发清除:
缺点:cms基于标记清除的算法实现的,所以内存碎片会产生过多。
G1收集器:
1、初始标记
2、并发标记
3、最终标记
4、筛选回收
基于标记-整理算法,不产生内存碎片
二者区别
G1收集器收集范围是老年代和新生代。不需要结合其他收集器使用;
CMS收集器是老年代的收集器,在新生代需要配合Serial和ParNew收集器一起使用
6、JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作 内存等。
- 重排序:为了提高性能,编译器和处理器会对执行进行重拍
- 主内存:共享变量存储的区域即是主内存
- 工作内存:每个线程copy的本地内存,存储了该线程读/写共享变量的副本
7、简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
类加载器 就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。
启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的, 或通过-Xbootclasspath 参数指定路径中的, 且被虚拟机认可(按文件名识别, 如 rt.jar) 的类。
扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类
库
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库
双亲委派:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父
类去完成,当父类加载器反馈自己无法完成这个请求的时候,子类加载器才会尝试自己去加载
打破:
1:自己写一个类加载器集成classload
2:重写loadclass方法
3:重写findclass方法
8、讲讲JAVA的反射机制。
Java程序在运行状态可以动态的获取类的所有属性和方法,并实例化该类,调用方法的功能
关于JAVA内存模型,一个对象(两个属性,四个方法)实例化100次,现在内存中的存储状态,几个对象,几个属性,几个方法。
由于JAVA中new出来的对象都是放在堆中,所以如果要实例化100次,将在堆中产生100个对象,一般对象与其中的属性、方法都属于一个整体,但如果 属性和方法是静态的,就是用static关键字声明的,那么属于类的属性和方法永远只在内存中存在一份。
线程私有区域
程序计数器:记录的是正在执行的虚拟机字节码指令的地址。所以它的长度是固定的,因此程序计数器内存区域是唯一一个在JVM规范中没有规定任何 OOM情况的区域。
Java虚拟机栈
本地方法栈 : 本地方法栈为虚拟机使用的Native方法服务
线程共享区域
Java堆
永久区:静态变量,
在运行时常量池中(永久区的一部分):final修饰
GC ROOTS(根搜索算法)
作为GC Roots的对象:虚拟机栈引用、永久代引用、本地方法栈引用
频繁GC的原因:
人为原因:System.gc()、创建太多弱引用类;
框架原因:框架内部调用了GC方法;
内存原因:heap大小设置比较小时,会引起频繁的GC
优化GC的配置
-XX:NewRatio=3:设置新生代:老年代=1:3
-XX:SurvivorRatio=8:设置新生代中Eden:Survivor=8:1
-Xms6144M -Xmx6144M:初始化堆内存大小、最大堆内存大小
-XX:PermSize=128M -XX:MaxPermSize=256M:永久代内存大小、最大永久代大小。
jconsole查看内存使用情况
java反编译
javap -c Swap.class