JVM虚拟机原理

JVM虚拟机原理

1.JVM内存区域

JVM内存区域包含如下:

  1. 方法区:用来存放加载的类信息、常量、静态变量、编译后的代码等数据
  2. 堆内存:JVM启动时创建,存放的是对象的实例,可以分为新生代和老年代。
  3. 虚拟机栈:一个线程单独开辟的空间,线程会执行一个或多个方法,一个方法对应一个栈帧,栈帧中存放的内容是局部变量、操作数栈、方法返回地址、动态链接、附加信息等
  4. 本地方法栈:和虚拟机栈的功能类似,是为虚拟机执行Native本地方法准备的。
  5. 程序计数器:用于记录当前程序执行字节码的位置,存储的是字节码指令地址。

其中虚拟机栈、本地方法栈、程序计数器3个内存区域是线程独占区域,当启用一个线程时就会开辟出这3个区域,线程结束之后这3个内存区域也就关闭了。
方法区和堆内存属于线程共享区域。

2.类加载机制

1、至少需要3个类加载器进行加载不同的类,分别是核心类库加载器(Bootstrap loader)、拓展类库加载器(Extension class loader)和应用程序加载器(application class loader)。

  1. 核心类库加载器负责加载保存在jdk/jre/lib/路径下的核心类库rt.jar包
  2. 拓展类库加载器负责加载在jre/lib/ext/路径下的拓展类包
  3. 应用程序加载器加载的是项目的class文件

2、双亲委派模型
该模型是为了避免类重复加载,也就是类在加载的时候,会从下到上逐级委托,从上倒下逐级查找。一级一级委托给最上级,然后从最上级的核心类库加载器进行查找对应的类,如果没有找到,则一级一级往下查找,查找到了对应的类则进行加载。
3、如何加载类

public static void main(String[] args) {
        try {
            URL[] urls=new URL[]{new URL("file:H:\\")};//类存放的位置
            URLClassLoader loader=new URLClassLoader(urls);
            Class clazz = loader.loadClass("ClassLoadTest");//根据类名称加载类
            System.out.println("输出ClassLoadTest类使用的类加载器-->"+clazz.getClassLoader());
            Object instance = clazz.newInstance();
            clazz.getMethod("test").invoke(instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.垃圾回收机制

1、标记对象是否回收的方法
  1. 引用计数
    当对象被引用之后将这个对象进行标记并加1,如果对象引用完成之后则减一,但是这种方法不能将互相引用的对象进行清除,例如A对象引用B对象,但又没有其他地方用到这2个对象,所以这2个也是需要清除的,但是又处于循环引用中,所以这种引用计数的方法不能把它们都清除掉。
  2. 可达性分析
    垃圾回收机制中会设置一些根对象(GCRoot),从根对象中开始查找类的引用,将所有被引用过的类都找出来并标记计数,而没有被引用过的类则全部会被清理掉。
  3. 对象的引用类型
    强引用:最常见的普通对象引用,只要还有强引用指向对象,就不会被回收
    软引用:JVM任务内存不足时,会去试图回收软引用指向的对象
    弱引用:虽然是引用,但是随时可能被回收
    虚引用:不同通过它访问对象,当对象被finalize后,执行指定逻辑的机制(Cleaner)
  4. 可达性的类型
    强可达:对象可以有一个或多个线程不通过各种引用访问到的情况
    软可达:当只能通过软引用访问到对象的状态
    弱可达:只能通过弱引用访问的状态,当弱引用被清除时,符合销毁条件
    幻可达:不存在其他引用,并且finalize过了,只有虚引用指向这个对象
    不可达:表示对象可以被清除
2、垃圾回收算法
  1. 标记-清除:
    将被标记为需要清理的类直接在内存中清除掉,这种方法的缺点就是会产生内存碎片,因为将需要清理的内存直接清除,会出现已经清理的内存不是连续的,如果有更大的类被创建,这时需要开辟更大的内存,然而干净的内存不是连续的,所以开辟不了更大的内存了,所以这些直接清理出来的内存就是内存碎片。
  2. 复制算法:
    复制一块相同的内存空间,将标记出计数的内存空间全部移动到新开辟的内存空间中,而之前的内存空间则全部清除。这种方法的缺点就是每次使用都要开辟新的内存空间,如果内存空间越大,开辟出新的内存空间就越大,会浪费内存空间。
  3. 标记-整理:
    在一块内存空间中,将标记为需要清理的内存全部清除之后,将剩下正在使用的内存进行整理,将这些内存整理在一起,这样就不会出现内存碎片了。缺点就是每次清除垃圾之后都要整理内存空间,会减缓清理速率。
3、JVM中的垃圾回收原理

在堆内存中开辟了一块内存空间用来专门处理垃圾回收,这块内存空间分为好几个区域,分别是新生代,S0,S1,老年代。
清理步骤:
首先新建的对象优先存放到新生代区域,如果是大对象,则直接存放到老年代。新生代区域中使用的是标记清除的算法,当新生代区域的对象被清理之后,存活的对象将会全部移动至S0内存区,S0和S1区域就用来使用复制方法,当对象被引用计数到8或者15次之后就会被移动到老年代区域,而这个引用次数可以进行设置。老年代区域使用的算法则是标记-整理。JVM中使用的这种垃圾回收方式将各种方法都混合在一起,可以有效的提高回收效率。

4、垃圾收集器
  1. 串行收集器 -Serial GC -XX:+UseSerialGC(新生代收集器):
    使用单个线程来处理垃圾收集工作,适合单处理器机器
    Client模式下JVM的默认选择。
  2. 串行收集器 -Serial Old-XX:+UseSerialOldGC(老年代收集器):
    可以在老年代使用,采用标记-整理的算法。
  3. 并行收集器 -Parallel GC -XX:UseParallelGC(新生代收集器):
    使用多线程来处理垃圾回收,可以设置GC时间或吞吐量等值
    server模式下JVM的默认选择
    吞吐量=用户代码运行时间/(用户代码运行时间+GC时间)
  4. 并行收集器 -Parallel Old GC -XX:UseParallelOldGC(老年代收集器):
    -XX:ParallelGCThreads:设置用于回收垃圾的线程数,通常情况下与CPU数量相同
    -XX:MAaxGCPauseMills:设置最大垃圾收集停顿时间,是一个大于0的整数
    -XX:GCTimeRatio:设置吞吐量大小,为0~100之间的整数
    -XX:+UseAdaptiveSizePolicy:打开自适应GC策略,以达到在堆大小、吞吐量、停顿时间之间的平衡点
    以上收集器存在一个stop the world的问题,在某个时间,程序会存在不能运行的情况。
  5. 并发收集器-CMS(Concurrent Mark Sweep) GC -XX:+UseConcMarkSweepGC
    专用于老年代的收集器,基于标记-清除算法,目的是尽量减少停顿时间
    原理是会尝试GC操作和用户线程一起执行,但是会抢占用户线程,更占用CPU资源
  6. 并行收集器-ParNew GC -XX:+UseParNewGC(新生代收集器,已经放弃维护)
    用于新生代GC的实现,实际上是串行收集器SerialGC的多线程版本
    经常用于配合老年代的CMS GC一起工作
    -XX:ParallelGCThreads 参数可用于设置多线程的数量
  7. 并发收集器-G1 -XX:+UseG1GC(新生代和老年代共用)
    针对于大堆内存设计的收集器,兼顾吞吐量和停顿时间,目的是替代CMS
    JDK9的版本中JVM的默认选择
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值