(1)虚拟机栈:存储局部变量表、操作数栈、动态链接、返回地址
加载:类的class文件读到内存,并为之创建一个java.lang.class对象
验证:文件格式验证、元数据验证、字节码验证、符号引用验证
准备:为类的静态变量分配内存,并设置默认初始值,例如int默认为0
解析:将类的二进制数据中的符号引用替换成直接引用
初始化:为类的静态变量赋予正确的初始值
PS:
符号引用:是以一组符号来描述所引用目标,符号可以是任何的字面形式的字面量;
直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。
(1)创建类的实例,也就是new一个对象
(2)访问某个类或接口的静态变量,或者对该静态变量赋值
(3)调用类的静态方法
(4)反射(Class.forName)
(5)初始化一个类的子类(会首先初始化子类的父类)
(6)JVM启动时标明的启动类,即文件名和类名相同的那个类
除此之外,下面几种情形需要特别指出:
对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。
(1)根类加载器(bootstrap class loader)
负责加载java核心类,并不继承java.lang.ClassLoader($JAVA_HOME中/jre/lib/rt.jar)
(2)扩展类加载器(extension class loader)
负责加载jre的扩展目录(/jre/lib/ext)
(3) 系统类加载器(system class loader)
负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径
(4)用户类加载器(user class loader)
先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
(1)新创建的对象实例在Eden区,如果创建对象大小超过jvm指定的阙值时,直接进老年代
(2)Eden区不够时,触发Yong GC,将幸存的对象存放至Servivor From或者Servivor To,存活年龄+1。当年龄到达阙值(默认15)时,进老年代。Yong GC采用复制算法
(3)老年代不够时,触发Full GC。Full GC采用标记整理算法。
(1)引用计数法
存在循环引用问题
(2) 可达性分析
通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
PS:
GC Root对象:
(1)java虚拟机栈引用的对象
(2)方法区中类静态属性引用的对象(一般指static修饰的对象,加载类的时候就加载到内存)
(3)方法区中常量引用的对象
(4)本地方法栈中的JNI(native)引用对象
(1)标记清除
(2)复制
(3) 标记整理
(1) Serial垃圾收集器(单线程、复制)
特性:标记整理 + 精准控制停顿时间
面试题:
栈是线程私有的,他的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表又包含基本数据类型,对象引用类型(局部变量表编译器完成,运行期间不会变化)。所以我们可以理解为栈溢出就是方法执行是创建的栈帧超过了栈的深度。那么最有可能的就是方法递归调用产生这种结果
如果没有Survior区,那么每一次gc都会进老年代, 那么垃圾收集会执行的很频繁,相当耗时。引入Survior区,目标就是鉴定“老不死”对象。
-Xms:初始堆大小
-Xmx:最大堆大小 建议Xmx与-Xmx相同,避免每次垃圾回收完成后JVM重新分配内存
-Xmn:年轻代大小 (堆=年轻代+年老代+永久代 增大年轻代会减小年老代)建议堆*3/8
-Xss:每个线程的堆栈大小 (小应用 栈浅128k/ 大应用256k 对性能影响比较大,需要严格的测试)
-XX:NewSize=n:年轻代大小
-XX:NewRatio=n:年老代:年轻代比值
-XX:SurvivorRatio=n:年轻代中 Eden:SFrom:STO=n:1:1
-XX:PretenureSizeThreshold:直接晋升到老年代的对象大小
-XX:MaxTenuringThreshold=0:垃圾最大年龄.
设置0 年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.
如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活 时间,增加在年轻代即被回收的概率.
-XX:PermSize:永久代(perm gen)初始值。默认为物理内存的1/64。
-XX:MaxPermSize:持久代最大值。物理内存的1/4。
内存等。
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -
XX:MaxTenuringThreshold=20XX:CMSInitiatingOccupancyFraction=80 -
XX:+UseCMSInitiatingOccupancyOnly
参考资料:
【史上最全阿里 Java 面试题总结】
【什么情况下会发生堆内存溢出,栈内存溢出,结合实例说明】
【JVM 新生代为何需要两个 Survivor 空间】
【常见JVM参数汇总】
【jvm垃圾回收算法及实现原理】