#Java—JVM
1.虚拟机的概念:在软件上模拟具有完全硬件功能的、运行在完全隔离的环境中的计算机系统。
常见的虚拟机 VMware、JVM、Virtual Box等
JVM和VMware、Virtural Box的区别
a.VMWare和Virtual Box是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器。
b.JVM是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,对其他寄存器进行了裁剪。
有很多的虚拟机,(JDK8)HotSpot 这个虚拟机是整合了HotSpot和Jrockit VM的优点。
Java内存区域划分:
将Java内存划分成两大区域:
1.线程私有
a.程序计数器:
概念:记录正在执行的线程虚拟机字节码指令的地址,如果正在执行的是一个Native方法,则计数器为空。
可以将它理解为行号计数器。
··为什么要有程序计数器?为什么这个计数器是私有的?
首先,多线程 通过轮流切换并分配处理器的方式来实现,在任何一个时刻,处理器都会执行一个线程的指令。因此为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各自的计数器互不影响。
**b.虚拟机栈:**局部变量表、操作数栈、动态链接、方法出口。
局部变量表:各种基本数据数据类型和对象引用,在编译期间完成分配,在执行期间不会改变局部变量的大小。
产生的异常:
StackOverFlowError :请求栈的深度大于虚拟机锁允许的深度 可以设置(-Xss设置栈的容量)
OutOfMemoryError(OOM):虚拟机在动态扩展的时候无法申请到足够多的的内存
c.本地方法栈:
本地方法栈为虚拟机使用的Native方法服务,而虚拟机栈为JVM执行的Java方法服务
2.线程共享
a.堆GC:是JVM分配内存的最大一块区域。是所有线程共享的一块区域。 对空间可以是不连续的
Java是Java垃圾回收器主要管理的一块内存,所以也叫GC堆,堆容量是可以扩展的(-Xmx设置最大值 -Xms设置最小值)
如果堆中没有足够的空间完成实例的分配并且堆也无法扩展时 抛出OOM异常
b.方法区:
方法区又称永久代在JDK8后被称为 元空间(MetaSpace) 存放 虚拟机加载的类信息、静态变量、常量、编译器编译后的代码。
c.运行时常量池
字面量:字符串(JDK1.7后移到堆中),final常量,基本数据类型的值。
符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。
Java heap OOM
public class Test{
static class OOMObject{}
public static void main(String[] args) {
List<OOMObject> list=new ArrayList<>();
while(true){
list.add(new OOMObject());
}
}
}
StackOverFlowError:
public class Test{
private static int num=0;
public static void main(String[] args) {
try {
print();
}finally {
System.out.println("栈的深度"+num);
}
}
public static void print(){
num++;
print();
}
}
Java垃圾回收器与内存分配策略
1.如何判断对象已死:
**a.引用计数法:**给对象增加一个引用计数器,每当有一个地方引用它,计数器加1;当引用失效时,计数器减1;任何时候计数器为0,说明对象没有再被使用,此时对象已死。
JVM没有用这个方法,原因是如果有循环引用时,计数器永远都不会减到1,那么对象永远不会被回收
public class Test{
private Object instance=null;
public static void main(String[] args) {
Test test1=new Test();//1
Test test2=new Test();//2
test1.instance=test2;//3
test2.instance=test1;//4
test1=null;//5
test2=null;//6
//强制进行垃圾回收
System.gc();//7
}
}
我们来分析上面的代码:
1.test1的计数器 count1=1;
2.test2的计数器,count2=1;
3.test1中的instance引用 test2 所以 test2=2
4.test2中的instance引用test1所以test1=2;
5.6 count1=1 count2=1;
7.System.gc() 注意 这个方法只是触发,什么时候回收由系统调度。
b.可达性分析算法:
通过一系列称为:“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索过的路径称为"引用链".当一个对象到GC Roots没有任何引用链连时(从GC Roots到这个对象不可达),证明此事对象是不可用的。
可作为GC Roots的对象:
1.虚拟机栈中引用的对象
2.方法区中静态属性引用的对象
3.方法区常量引用的对象
4.本地方法栈中JNI(Native方法)引用的对象,
JDK1.2之后对Java引用的概念做了扩充,将引用分为四种 强引用(Strong Reference) 软引用(Strong Reference) 弱引用(Weak Reference) 虚引用(Phantom Reference) 强度依次递减
1.强引用:在程序中普遍存在的引用,只要引用还在,这个对象永远不会被回收
2.软引用:描述一些还有但是不是 必须的对象,在系统要发送内存溢出之前,将这类引用列入回收范围,以供第二次回收,如果这次回收空间还是不够,那么抛异常OOM。
3.弱引用:描述非必须的对象,这些对象只能生存到下次gc之前,垃圾回收器开始后无论内存是否够用,都要直接回收。
4.虚引用:一个对象是否由虚引用不会对其生存时间够成影响,也无法通过虚引用来取得一个实例化对象,只会在被gc回收后首到一个系统通知。
finalize方法:
在可达性算法分析对象不可达的对象也不是非死不可的, 它至少要有两次标记过程:如果对象在进行可达性分析之后发现没有GC Roots相连接的引用链,那它会被第一次标记并且进行一次筛选,筛选的条件是此对象是要执行finalize(),当finalize没有被覆盖或者已死已经被JVM调用过了,这时对象才会真正被回收。
回收方法区:
方法区的垃圾回收主要是回收两个部分:无用类和无用常量。
如何判断类是无用类:
a.该类的实例对象都已经被回收(在Java堆中不存在任何该类有关的实例)
b.加载该类的classloaer已经被回收
c.该类对应的Class对象没有任何其他方法被引用,无法在任何地方通过反射访问该方法
注:在大量使用反射、动态代理等场景都需要JVM具备类卸载的功能防止永久代的溢出。
垃圾回收算法
标记—清除算法
复制算法(新生代算法)
标志整理算法(老年代算法)