JVM-内存模型与垃圾回收

本文转载:https://blog.youkuaiyun.com/weixin_45676630/article/details/105799329

但没有转载完全,gc算法之前已经写过了

1.内存模型

自写的简单基础结构jvm流程

1.1 JVM 运行时内存

Java 堆从GC 的角度可以细分为: 新生代(Eden 区、From Survivor 区和To Survivor 区)和老年
代。

在这里插入图片描述

1.1.1 young区

是用来存放新生的对象。一般占据堆的1/3 空间。由于频繁创建对象,所以young区会频繁触发MinorGC 进行垃圾回收。young区又分为 Eden 区、ServivorFrom、ServivorTo 三个区。

1.1.1.1 Eden 区

Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到old区)。当Eden 区内存不够的时候就会触发MinorGC,对young区进行一次垃圾回收。正常对象创建所在区域,大多数对象“朝生夕死”

1.1.1.2 ServivorFrom

上一次GC 的幸存者,作为这一次GC 的被扫描者。

1.1.1.3 ServivorTo

保留了一次MinorGC 过程中的幸存者。在同一个时间点上,ServivorFrom和ServivorTo只能有一个区有数据,另外一个是空的。

1.1.1.44 MinorGC 的过程

MinorGC 采用复制算法。

  • eden、servicorFrom 复制到 ServicorTo,年龄+1
    首先,把Eden 和ServivorFrom区域中存活的对象复制到ServicorTo 区域(如果有对象的年龄以及达到了old区的标准,则赋值到old区),同时把这些对象的年龄+1(如果ServicorTo 不够位置了就放到old区);
  • 清空eden、servicorFrom
    然后,清空Eden 和ServicorFrom 中的对象;
  • ServicorTo 和 ServicorFrom互换
    最后,ServicorTo 和ServicorFrom 互换,原ServicorTo 成为下一次GC 时的ServicorFrom区。

在这里插入图片描述

1.1.2 old区

主要存放应用程序中生命周期长的内存对象。old区的对象比较稳定,所以MajorGC 不会频繁执行。在进行MajorGC 前一般都先进行了一次MinorGC,使得有young的对象晋身入old区,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC 进行垃圾回收腾出空间。MajorGC 采用标记清除算法:首先扫描一次所有old区,标记出存活的对象,然后回收没有标记的对象。MajorGC 的耗时比较长,因为要扫描再回收。MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当old区也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

1.2 借助工具体验

插件下载链接:https://visualvm.github.io/pluginscenters.html

① 运行命令 jvisualvm

打开工具界面:

在这里插入图片描述

在插件中选择上面地址中下载的插件

在这里插入图片描述

从这个图中可以看出堆的区域划分,也证明了前面说的ServivorFrom和ServivorTo只能有一个区有数据,另外一个是空的。即图中的S0和S1.
在这里插入图片描述

1.2.1 内存溢出的配置体验

堆内存溢出案例:
设置参数:-Xmx20M -Xms20M
测试代码:

	public String heap() throws Exception{
        while(true){
            list.add(new Person());
        }
    }

在这里插入图片描述

方法区内存溢出案例:
设置参数:-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M
测试代码:

	public String heap(){
        while(true){
            list.addAll(MetaspaceUtil.createClasses());
        }
    }

在这里插入图片描述

public class MetaspaceUtil extends ClassLoader {

    public static List<Class<?>> createClasses() {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
                    "java/lang/Object", null);
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                    "()V", null, null);
            mw.visitVarInsn(Opcodes.ALOAD, 0);
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
                    "<init>", "()V");
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();
            MetaspaceUtil test = new MetaspaceUtil();
            byte[] code = cw.toByteArray();
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
            classes.add(exampleClass);
        }
        return classes;
    }
}

虚拟机栈溢出案例:

测试代码:

	public static void method(long i){
        System.out.println(count++);
        method(i);
    }

在这里插入图片描述

2 垃圾回收

2.1 如何确定一个对象是垃圾

  • (1)引用计数法
    在Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单
    的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关
    联的引用,即他们的引用计数都不为0,则说明对象不太可能再被用到,那么这个对象就是可回收
    对象。
  • (2)可达性分析
    由GC Root出发,开始寻找,看看某个对象是否可达,如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。
    GC Root:类加载器、Thread、本地变量表、static成员、常用引用、本地方法栈中的变量等。
    要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记
    过程。两次标记后仍然是可回收对象,则将面临回收。

2.2 垃圾回收算法

3.垃圾收集器

3.1如何选择收集器

  • 优先调整堆的大小让服务器自己选择
  • 如果内存小于100M,使用串行收集器
  • 如果是单核,并且没有停顿时间要求,使用串行或JVM自己选
  • 如果允许停顿时间超过1秒,选择并行或JVM自己选
  • 如果响应时间最重要,并且不能操作1秒,使用并发收集器

3.2如何开启收集器

  • ①串行
-XX: +UseSerialGC
-XX: +UseSerialoldGC
  • ②并行(吞吐量优先)
-XX: +UseParallelGC
-XX: +UseParalleloldGC
  • ③并发收集器(响应时间优先)
-XX: +UseConcMarkSweepGC
-XX: +UseG1GC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值