运行时数据区:有亮色的有灰色的,灰色的就是占得内存非常小,几乎不存在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());
}
}
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);
}
}
栈中主要存储3类数据:
本地变量:输入参数和输出参数,方法内的变量
栈操作:记录出栈、入栈的操作
栈帧数据:包括类文件、方法等。
堆分为三部分:新生区,养老区,永久区
新生区分为:伊甸区、幸存者0区、幸存者1区
java8把永久区改为元空间
(物理上堆分为新生区、养老区;逻辑上分为新生区、养老区、元空间)
-Xms:start起始内存
-Xmx:max最大内存
-Xmn:一般不会调这个参数!
java8中,永久代被移除,被元空间取代,元空间的本质和永久代类似。
元空间与永久代之间最大的区别在于:
永久带使用的JVM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。
因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少的类的元数据就不再有MaxPermSize控制,而由系统的实际可用空间来控制。
默认
-Xms:为物理内存大小的1/64
-Xmx:为物理内存大小的1/4
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");
}
}
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
运行:
上图: 新生代+老年代的内存大小等于981.5MB,元空间用的是物理内存空间!
分代收集算法:
当Eden区满了,会回收,没有被回收的会和survivorFrom区没有被回收的使用复制算法复制到survivorTo区,然后from区和Eden区全部清空,然后from区和to去进行交换(from区变成to区,to区变成from区,所以说谁空谁是to),对象没熬过一次Minor GC年龄就会+1,当对象年龄达到默认设置的15
(-XX:MaxTenuring Threshold参数来设置)那么就会被送到老年代。
当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行
原理:
工作中实际使用:(标记压缩,多次GC后才会进行清除)
但是有个G1垃圾收集算法: Garbage First(G1) 垃圾收集器(目前就不往下深入学习了,这里留个问题,以后再学习)