jvm内存模型
1、程序计数器:几乎不占内存,用于取下一条指令
2、堆:所有通过new创建的对象内存都在堆中分配
3、虚拟机栈:每个线程在执行每个方法时都会申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数、中间结果
4、本地方法栈:存储每个native方法调用的状态
5、方法区(元空间):用于存放类信息、静态变量、final常量、属性和方法信息,jvm用永久代存放
什么是新生代
1、新生代由eden区、from survivor区和to suivivor区组成,内存占比默认为80%、10%、10%
2、新建的对象都由新生代分配内存,eden空间不足时,会把存活的对象转移到survivor
3、新生代由于存活时间短,所以GC比较频繁
什么是老年代
1、用于存放在新生代多次回收依然存在的对象
2、新生代经过一次minorGC后,如果对象还存活,并且能够被另一块survivor内存所容纳,则使用复制算法把这些依旧存活的对象复制到另一块survivor中,然后清理eden和第一块survivor区域,同时将这些对象的年龄设置为1,之后survivor区域每熬过一次minorGC,就将这些对象的年龄加1,当对象的年龄达到某个值(默认15)后,这些对象就会成为老年代
3、对于一些较大的对象,直接进入老年代。比如对象大小超过eden空间或者survivor空间。或者对象比较大,为了避免eden到survivor端的大量复制,提前进入老年代
新生代和老年代在jvm中的内存空间占用比例分配
新生代1/3,老年代2/3
eden和from survivor、to survivor在内存中的空间占用比例
eden80%,from survivor 10%,to survivor 10%
新生代的回收算法
复制算法:扫描出存活的对象,然后复制到一块新的完全未使用的空间中。对应于新生代:就是在Eden和其中一个Survivor,复制到另一个之间Survivor空间中,然后清理掉原来就是在Eden和其中一个Survivor中的对象。
老年代的回收算法
标记-清除算法:对没有引用,需要回收的对象进行标记,然后统一回收
缺点:效率问题;空间问题
什么是minorGC
Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法:
什么是fullGC
Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法:
jvm内存清理过程
用javavisualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装载,当旧生代也满了后,就会报outofmemory的异常。
jvm参数
-Xms 初始堆大小。如:-Xms256m
-Xmx 最大堆大小。如:-Xmx512m
-Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-Xss JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。
-XX:NewRatio 新生代与老年代的比例,如
–XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
-XX:PermSize 永久代(方法区)的初始大小
-XX:MaxPermSize 永久代(方法区)的最大值
-XX:+PrintGCDetails 打印 GC 信息
-XX:+HeapDumpOnOutOfMemoryError 让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分
1、什么是字节码?使用字节码的好处是什么?
java程序通过编译器编译成字节码文件,也就是计算机可以识别的二进制
采用字节码的最大好处是:可以实现一次编译到处运行,也就是java的与平台无关性
2、java运行时一个类是什么时候被加载的
虚拟机自由实现,hotspot虚拟机是按需加载
3、jvm类加载过程(类声明周期)
加载-验证-准备-解析-初始化-使用-卸载
4、继承时父子类的初始化顺序
父类--静态变量
父类--静态代码块
子类--静态变量
子类--静态代码块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
5、jvm有哪些类加载器?JVM三层类加载器之间的关系是继承吗
bootstrapClassLoader,加载<JAVA_HOME>\jre\lib\rt.jar,resource.jar,charsets.jar
extensionClassLoader,加载<JAVA_HOME>\jre\lib\ext
ApplicationClassLoader,加载用户classpath路径上所有的类库
自定义类加载器
三层类加载器不是继承关系
6、什么是类加载器的双亲委派机制
如果一个类加载器收到了类加载的请求,他首先不会尝试去加载这个类,而是会把这个请求委派给父加载器去完成,每一个层次的类加载器都是如此,因此所有的类加载请求最后都应该传送到最顶层的启动类加载器中,只有当上一层类加载器反馈自己无法完成这个加载请求时(搜索范围没找到这个类)下一层类加载器才会尝试自己去加载
7、jdk为什么要设计双亲委派模型,有什么好处?
1、确保安全,避免java核心类库被修改(比如自定义了String类,通过双亲委派机制加载,始终加载到的都是jdk提供的String类)
2、避免重复加载
3、保证类的唯一性
8、可以打破jvm的双亲委派模型吗?如何实现
因为双亲委派模型是通过loadClass方法实现的,可以自定义一个类加载器,重写loadClass方法就可以绕过双亲委派机制
9、如果定义自己的类加载器
继承抽象类ClassLoader
覆盖findClass方法或者loadClass方法
10、java代码是如何运行起来的
通过编译器将源代码编译为字节码文件,然后通过jvm的解释器把字节码解释为机器码,cpu执行指令
11、jvm哪些区域是线程共享的?哪些区域是线程私有的
元空间和堆是线程共享的,其他区域是线程私有的
12、jvm如何判断可以被回收
可达性分析算法,判断对象是否存活
实现原理:通过一系列称为GC ROOTS的根对象作为起始节点,从这些结点开始,根据引用对象向下搜索,搜索过程经过的路径成为引用链,如果某个对象到GCroots之间没有任何引用链相连(不可达),则证明此对象不可能再被使用, 就可以被垃圾回收期回收
可以作为GCROOTS的对象:虚拟机栈中被引用的对象;方法区中静态变量引用的对象;方法去中常量引用的对象;本地方法栈中引用的对象;java虚拟机内部的引用:基础类型引用的对象、异常对象等;被同步锁持有的对象;
13、jvm中的垃圾回收算法,分别解释一下
1、标记清除算法。
分为标记和清除两个阶段。标记所有需要清除的对象,基于可达性算法判断对象是否可以回收。标记后对所有标记的对象进行清除
优点:基于可达性算法,实现简单
缺点:执行效率不稳定。如果有大量对象需要回收,导致标记和清除两个过程执行效率都随对象数量增加而降低;内存空间碎片化,如果后续有大对象需要创建时找不到足够大的连续空间,导致需要提前开始一次垃圾回收
2、标记复制算法
将内存划分为大小相同的两块,每次使用其中一块,当一块用完以后,将还存货的对象复制到另一块内存上,然后再把已使用过的内存空间清理一次
优点:实现简单,效率高,解决了标记-清除算法导致的内存碎片问题
缺点:代价太大,浪费一般内存空间;对象存活率较高时,效率低
3、标记整理算法
标记:也是基于可达性分析算法进行标记。
整理:根据存活对象进行整理,让存活对象都向一端移动,然后直接清理掉边界以外的内存
优点:提高了空间利用率,不会产生内存碎片
缺点:效率低
4、分代收集算法
根据对象存活周期的不同,将内存划分为新生代、老年代等不同的几块,jvm根据各个年代的特点采用不同的收集算法。一种思想,综合了各种垃圾回收算法
新生代中,每次都有大量对象死去,所以采用复制算法。老年代中,因为对象存活率高,所以采用标记清理,标记整理算法进行回收
14、什么是内存泄漏?什么是内存溢出?
内存泄漏:程序运行后,没有释放所占用的内存空间。一次不会有太大影响,时间长会导致内存溢出
内存溢出:程序在申请内存时,没有足够的内存供其使用