JVM

JVM(Java Virtual Machine)Java虚拟机

体系结构:

Java虚拟机主要分为五大模块:类装载器子系统、运行时数据区、执行引擎、本地方法接口和垃圾收集模块。

类加载器

定义:
类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因。在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字节流,完成这个动作的代码块就是类加载器。这一动作是放在Java虚拟机外部去实现的,以便让应用程序自己决定如何获取所需的类。

分类:
1.启动类(或根类)加载器(Bootstrap ClassLoader):
这个加载器不是一个Java类,而是由底层的c++实现,负责将存放在JAVA_HOME下lib目录中的类库,比如rt.jar。因此,启动类加载器不属于Java类库,无法被Java程序直接引用。

2.扩展类加载器(ExtClassLoader):
由sun.misc.Launcher$ExtClassLoader实现,负责加载JAVA_HOME下lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

3.应用类加载器(AppClassLoader):
由sun.misc.Launcher$AppClassLoader实现的。由于这个类加载器是ClassLoader中的getSystemClassLoader方法的返回值,所以也叫系统类加载器。它负责加载用户类路径上所指定的类库,可以被直接使用。如果未自定义类加载器,默认为该类加载器。

双亲委派模型
定义:当一个类加载器接收到类加载请求时,会先请求其父类加载器加载,依次递归,当父类加载器无法找到该类时(根据类的全限定名称),子类加载器才会尝试去加载。

意义:双亲委派模型是为了保证Java核心库的类型安全。所有Java应用都至少需要引用java.lang.Object类,通过双亲委派模型,对于Java核心库的类的加载工作由启动类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。

运行时数据区
在这里插入图片描述

JVM参数

标准参数:
以-开头,如:java -version、java -jar等,通过java -help可以查询所有的标准参数。见官网 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
非标准参数:
以-X开头,可以通过 Java -X 命令来检索所有-X 参数。

 -Xmn新生代内存的大小值,包括Eden区和两个Survivor区的总和
 写法如:-Xmn1024,-Xmn1024k,-Xmn1024m,-Xmn1g 。
 
 -Xms堆内存的最小值,默认值是总内存/64(且小于1G),默认情况下,当堆中可用内存
 小于40%(这个值可以用-XX: MinHeapFreeRatio 调整,如-XX:MinHeapFreeRatio=30)时,堆内存会开始增加,一直增加到-Xmx的大小。
 
 -Xmx堆内存的最大值,默认值是总内存/64(且小于1G),如果Xms和Xmx都不设置,
 则两者大小会相同,默认情况下,当堆中可用内存大于70%时,堆内存会开始减少,一直减少到-Xms的大小;
 
 整个堆的大小=年轻代大小+年老代大小,堆的大小不包含持久代(元空间)大小,
 默认年老代大小/年轻代大小=2/1左右
 
-Xss每个线程的栈内存,默认1M,一般来说是不需要改的。

-Xrs减少JVM对操作系统信号的使用。

-Xprof跟踪正运行的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。
 
-Xnoclassgc关闭针对class的gc功能;因为其阻止内存回收,所以可能会导致OutOfMemoryError错误,慎用;

-Xincgc开启增量gc(默认为关闭);这有助于减少长时间GC时应用程序出现的停顿;但由于可能和应用程序并发执行,所以会降低CPU对应用的处理能力。

-Xloggc:file与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。

不稳定参数:
以-XX:+ 、-XX:- 开头

垃圾回收机制:

五大区域中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。垃圾回收主要是Java堆区和方法区。

垃圾回收算法
标记-清除算法:
标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。
优点:标记-清除算法不需要进行对象的移动,只需对不存活的对象进行处理,在存活对象比较多的情况下极为高效
缺点:由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。
在这里插入图片描述
在这里插入图片描述

复制算法:
复制算法的提出是为了克服句柄的开销和解决内存碎片的问题。它开始时把堆分成 一个对象面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,就扫描找到存活对象,并将每个存活对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。
优点:清理效率高
缺点:浪费空间,空间利用率低
在这里插入图片描述
在这里插入图片描述

标记-整理算法(标记清除压缩算法):
  标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。
  
分代收集算法:
  分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation)。老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
年轻代(Young Generation)的回收算法
a) 所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

b) 新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。

c) 当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。

d) 新生代发生的GC也叫做Minor GC,MinorGC发生频率比较高(不一定等Eden区满了才触发)。

年老代(Old Generation)的回收算法
a) 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

b) 内存比新生代也大很多(大概比例是1:2),当老年代内存满时触发Major GC即Full GC,Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高。

持久代(Permanent Generation)的回收算法
  用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
  
垃圾收集器
Serial收集器:复制算法、单线程
在这里插入图片描述

ParNew收集器:复制算法、多线程
在这里插入图片描述

Parallel Scavenge 收集器:复制算法、并行多线程
Parallel Old 收集器:标记-整理算法
在这里插入图片描述

CMS 收集器:标记-清除算法
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值