JVM

定义

java虚拟机:介于java程序与操作系统之间,将java字节码文件解释编译为操作系统可执行的二进制机器码。不同的操作系统在安装jdk时,里面会有对应此操作系统的JVM,这也是java代码能够“一次编写,到处运行”的原因。

概述

.java文件经过javac命令后被编译为.class字节码文件,再由JVM中的类加载器加载到运行时数据区,再由执行引擎进行执行。

双亲委派机制

当一个.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

JVM所管理的内存包括以下几个运行时数据区。

  • 程序计数器
    很小的一块内存区域,用来指向当前线程所执行到的字节码的行号。是线程所私有的,每条线程都需要有一个独立的程序计数器。在每条字节码指令执行完之后,由执行引擎对其进行增加操作。
  • Java虚拟机栈
    JVM会为每个线程分配一个虚拟机堆栈,所以和程序计数器一样也是线程私有的,用来描述Java方法的执行。线程中每个方法执行时,都会创建一个栈帧,里面存放局部变量表,操作数栈,动态链接,方法出口等信息。
  • 本地方法栈
    与Java虚拟机类似,区别在于执行的是本地方法(Native)。

  • JVM中内存最大的一块,是各个线程共享的,用来存放对象实例。
  • 方法区
    也是线程共享的,主要存放类元信息,静态变量,常量等。在1.8以前称为永久代,在1.8以后称为元空间,在1.7时把字符串常量池移入了堆中。永久代和元空间都是方法区的实现,最大区别在于永久代使用的是JVM的内存,元空间使用的是本地内存。

在这里插入图片描述

GC

堆是发生GC的主要区域,分为年轻代(Young Gen)和老年带(Old Gen),年轻代又分为伊甸园区(Eden)和幸存区(Survivor 1、2)。

判断对象是否存活

  • 引用计数法
    每个对象都会自带一个引用计数器,当有对象引用自己时则+1,取消引用时则-1,当计数器为0时,则将对象标记为垃圾。但当两个对象互相引用时,会造成循环而导致内存泄露。
  • 可达性分析
    通过GCRoots(栈中变量/方法区静态变量/常量等)向下寻找,走过的路径称做引用链,当一个对象到GCRoots没有任何引用链即根不可达时,该对象被判断为垃圾。

垃圾回收算法

  • 标记-清除
    标记阶段,通过可达性算法标出所有存活对象;清除阶段,对所有未标记的对象进行统一回收。
    缺点:效率不高;会产生内存碎片。
  • 复制算法
    将内存分为两个区域,每次只使用其中一个用来存储活着的对象。发生GC时,将所有存活的对象移动到另外一个未使用的区域,再统一回收之前的区域。
    缺点:不适应于对象存活率高的情况;会浪费一半的内存。
  • 标记-整理
    在标记-清除的基础上,进行了内存压缩,即将所有活着的对象移动到内存的一端。
    缺点:需要移动对象,效率不高。

Minor GC和Full GC

  • Minor GC
    对Eden区和Survivor区进行垃圾回收;
    因为Java对象大多是朝生夕灭,所以Minor GC非常频繁;
  • Full GC
    对整个堆区进行垃圾回收;

吞吐量

吞吐量=用户代码运行时间 / 处理器总消耗时间
即:
吞吐量 = 用户代码运行时间 / (用户代码运行时间 + 垃圾收集时间)

STW

stop-the-world:在发生GC时,所有用户线程都需要挂起,进入阻塞状态。

垃圾回收器

  • 新生代收集器:Serial、ParNew、Parallel Scavenge;
  • 老年代收集器:Serial Old、Parallel Old、CMS;
  • 整堆收集器:G1
  1. Serail
    单线程串行收集器,工作在新生代,使用复制算法进行回收,在回收时会stw。与Serial Old搭配使用。
    在这里插入图片描述

  2. ParNew
    实质是Serial收集器的多线程并行版本,工作在新生代,使用复制算法。与CMS搭配使用。

  3. Parallel Scavenge
    新生代收集器,基于复制算法,并行收集的多线程收集器。其目标是达到一个可控制的吞吐量。

  4. Serail Old
    Serail收集器的老年代版本,单线程收集器,使用标记-整理算法。

  5. Parallel Old
    Parallel收集器的老年代版本,并行收集的多线程收集器。基于标记-整理算法。
    在这里插入图片描述

  6. CMS
    Concurrent Mark Sweep收集器是以获取最短回收停顿时间为目标的收集器,工作在老年代,基于标记-清除算法。
    在这里插入图片描述
    缺点:内存碎片,而且会产生“浮动垃圾”,因为在并发清理时,用户线程还在运行,此时就会有新的垃圾对象产生,而这部分垃圾对象是在标记过程结束后出现的,所以只能等到下一次垃圾收集时再清理。CMS还需要给并发收集时其他线程预留内存空间,若是预留的内存无法满足新对象的需要,就会出现“并发失败”,开启后备预案,启用Serail Old收集器,性能反而会降低。

  7. G1
    G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在了。
    在这里插入图片描述
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值