一些常见面试题:
JVM的位置(运行在操作系统上,与硬件没有直接的交互)
一、jvm体系结构(记住背下来)
运行时数据区:有亮色的有灰色的,灰色的就是占得内存非常小,几乎不存在GC垃圾回收,并且线程独占的,亮色的存在垃圾回收,并且所有线程共享。
二、类装载器
(看做成快递员,把class文件(class文件开头有特定的文件标示cafe babe)字节码加载到内存,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于是否可以运行,由Execution Engine决定)
类装载器有哪几个?
1-启动类加载器,负责加载%JAVA_HOME%\bin目录下的所有jar包,或者是-Xbootclasspath参数指定的路径;
2-扩展类加载器:负责加载%JAVA_HOME%\bin\ext目录下的所有jar包,或者是java.ext.dirs参数指定的路径;
3-应用程序类加载器:负责加载用户类路径上所指定的类库,如果应用程序中没有自定义加载器,那么次加载器就为默认加载器。
加载器之间的层次关系:
public class MyObject {
public static void main(String []args){
Object object = new Object();
MyObject myObject = new MyObject();
System.out.println(object.getClass().getClassLoader());
System.out.println(myObject.getClass().getClassLoader());
}
}
运行结果:启动类加载器来加载java自带的类,BootStrap是C++写的所以输出的null;
package JVM;
public class MyObject {
public static void main(String []args){
Object object = new Object();
System.out.println(object.getClass().getClassLoader());
System.out.println();
System.out.println();
System.out.println();
MyObject myObject = new MyObject();
System.out.println(myObject.getClass().getClassLoader());
System.out.println(myObject.getClass().getClassLoader().getParent());
System.out.println(myObject.getClass().getClassLoader().getParent().getParent());
}
}
双亲委派机制:
双亲委派机制得工作过程:
1-类加载器收到类加载的请求;
2-把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器(bootstrap);
3-启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛出异常,通知子加载器进行加载。
4-重复3;
当加载一个类会先到启动类加载器去找,找得到就用,找不到就到扩展类加载器找,找不到再去应用程序类加载器去找。(从顶层往下开始找)
双亲委派机制的理由?
是沙箱安全机制
例:
String类,String是java.lang包下的类,默认情况下是启动类加载器进行加载的。假设我也自定义一个String。现在你会发现自定义的String可以正常编译,但是永远无法被加载运行。
这是因为申请自定义String加载时,总是启动类加载器,不会是其他的加载器,也就是不会用应用程序加载器加载。
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println(1111);
}
}
运行结果:是无法被加载的
三、本地方法栈(加载native方法,了解)
四、程序计数器(类似一个指针,一条指令执行完用来指向下一个指令 )
五、方法区(类的模板工厂)
六、java栈(灰色,线程私有,不存在gc )
栈中主要存储3类数据:
本地变量:输入参数和输出参数,方法内的变量
栈操作:记录出栈、入栈的操作
栈帧数据:包括类文件、方法等。
七、堆(一个jvm实例只存在一个堆空间,大小可以调节)
堆分为三部分:新生区,养老区,永久区
新生区分为:伊甸区、幸存者0区、幸存者1区
java8把永久区改为元空间
(物理上堆分为新生区、养老区;逻辑上分为新生区、养老区、元空间)
java7之前, java8把永久区换成了元空间()
详细版:复制 -> 清空 -> 交换
对象生命周期和GC
jvm堆参数调优:
-Xms:start起始内存
-Xmx:max最大内存
-Xmn:一般不会调这个参数!
java8中,永久代被移除,被元空间取代,元空间的本质和永久代类似。
元空间与永久代之间最大的区别在于:
永久带使用的JVM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。
因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少的类的元数据就不再有MaxPermSize控制,而由系统的实际可用空间来控制。
默认
-Xms:为物理内存大小的1/64
-Xmx:为物理内存大小的1/4
jvm参数调优:实际-Xms和-Xmx大小必须一致,防止GC和应用程序争抢内存,理论值的峰值和峰度忽高忽低。
先查看内存大小:
package JVM;
public class heap {
public static void main(String[] args) {
long maxMemory = Runtime.getRuntime().maxMemory();//返回java虚拟机试图使用的最大内存容量
long totalMemory = Runtime.getRuntime().totalMemory();//返回java虚拟机的总内存容量
System.out.println("-Xmx:MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
System.out.println("-Xms:TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
}
}
更改-Xms和-Xmx参数:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
运行:
上图: 新生代+老年代的内存大小等于981.5MB,元空间用的是物理内存空间!
GC 垃圾收集机制(分代回收算法)
分代收集算法:
- 次数上频繁收集Young区
- 次数上较少收集Old区
- 基本不动元空间
GC 4个算法
1.引用计数法
2. 复制算法