04-JVM虚拟机-课堂笔记

04-JVM虚拟机

1. JVM虚拟机概述

1.4 对象的创建流程与内存分配

1.4.1 创建流程

在这里插入图片描述

1.4.2 对象内存分配方式

内存分配的方法有两种:不同垃圾收集器不一样

  • 指针碰撞(Bump the Pointer)

  • 空闲列表(Free List)

分配方法 说明 收集器
指针碰撞(Bump the Pointer) 内存地址是连续的(新生 代) Serial 和 ParNew 收集器
空闲列表(Free List) 内存地址不连续(老年代) CMS 收集器和 Mark-Sweep 收集 器

在这里插入图片描述

指针碰撞示意图:

在这里插入图片描述

1.4.3 内存分配安全问题

在分配内存的时候,虚拟机给A线程分配内存过程中,指针未修改。此时B线程同时使用了同样一块内存。是不是就出现了线程的安全性问题?

在JVM中有两种解决办法:

  1. CAS 是乐观锁的一种实现方式。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。

  2. TLAB本地线程分配缓冲(Thread Local Allocation Buffer即TLAB):为每一个线程预先分配一块内存

JVM在第一次给线程中的对象分配内存时,首先使用CAS进行TLAB的分配。当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配。

对象内存分配流程【重要】:

在这里插入图片描述

1.4.4 对象怎样才会进入老年代?重点

内存担保机制是个怎么回事?

对象内存分配:

  • 新生代:新对象大多数都默认进入新生代的Eden区

  • 进入老年代的条件:四种情况

    • 存活年龄太大,默认超过15次【-XX:MaxTenuringThreshold】
    • 动态年龄判断:MinorGC之后,发现Survivor区中的一批对象的总大小大于了这块Survivor区的50%,那么就会将此时大于等于这批对象年龄最大值的所有对象,直接进入老年代
      • 举个栗子:Survivor区中有一批对象,年龄分别为年龄1+年龄2+年龄n的多个对象,对象总和大小超过了Survivor区域的50%,此时就会把年龄n及以上的对象都放入老年代。
      • 为什么会这样?希望那些可能是长期存活的对象,尽早进入老年代。
      • -XX:TargetSurvivorRatio可以指定
    • 大对象直接进入老年代:前提是Serial和ParNew收集器
      • 举个栗子:字符串或数组
      • -XX:PretenureSizeThreshold一般设置为1M
      • 为什么会这样?为了避免大对象分配内存时的复制操作降低效率。避免了Eden和 Survivor区的复制
    • MinorGC后,存活对象太多无法放入Survivor

空间担保机制:当新生代无法分配内存的时候,我们想把新生代的老对象转移到老年代,然后把新对象放入腾空的新生代。此种机制我们称之为内存担保。

  • MinorGC前,判断老年代可用内存是否小于新时代对象全部对象大小,如果小于则继续判

  • 断判断老年代可用内存大小是否小于之前每次MinorGC后进入老年代的对象平均大小

  • 如果是,则会进行一次FullGC,判断是否放得下,放不下OOM

  • 如果否,则会进行一些MinorGC:

    • MinorGC后,剩余存活对象小于Survivor区大小,直接进入Survivor区
    • MinorGC后,剩余存活对象大于Survivor区大小,但是小于老年代可用内存,直接进入老年代
    • MinorGC后,剩余存活对象大于Survivor区大小,也大于老年代可用内存,进行FullGC
    • FullGC之后,任然没有足够内存存放MinorGC的剩余对象,就会OOM

老年代的担保示意图:

在这里插入图片描述

1.4.5 案例演示:对象分配过程

01-大对象直接进入老年代

package com.hero.jvm.object;

/**
 * 测试:大对象直接进入到老年代
 * -Xmx60m -Xms60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:PretenureSizeThreshold
 */
public class YoungOldArea {
   
   
    public static void main(String[] args) {
   
   
        byte[] buffer = new byte[1024 * 1024 * 20]; // 20M
    }
}
  • -XX:NewRatio=2 新生代与老年代比值

  • -XX:SurvivorRatio=8 新生代中,Eden与两个Survivor区域比值

  • -XX:+PrintGCDetails 打印详细GC日志

  • -XX:PretenureSizeThreshold 对象超过多大直接在老年代分配,默认值为0,不限制

在这里插入图片描述

02-对象内存分配的过程:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * -Xmx600m -Xms600m -XX:+PrintGCDetails
 */
public class HeapInstance {
   
   
    public static void main(String[] args) {
   
   
        List<Picture> list = new ArrayList<>();
        while (true) {
   
   
            try {
   
   
                Thread.sleep(20);
            } catch (InterruptedException e) {
   
   
                e.printStackTrace();
            }
            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }
    }
}

class Picture {
   
   
    private byte[] pixels;

    public Picture(int length) {
   
   
        this.pixels = new byte[length];
    }
}

在这里插入图片描述

在这里插入图片描述

1.4.6 案例演示:内存担保机制

案例准备

  • JVM参数: -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 - XX:+UseSerialGC

  • 分配三个1MB的对象和一个5MB的对象

  • -Xmn10M新生代内存的最大值:包括Eden区和两个Survivor区的总和

代码如下:

/**
 * 内存分配担保案例
 */
public class MemoryAllocationGuarantee {
   
   
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
   
   
        memoryAllocation();
    }

    public static void memoryAllocation() {
   
   
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[1 * _1MB]; // 1M
        allocation2 = new byte[1 * _1MB]; // 1M
        allocation3 = new byte[1 * _1MB]; // 1M
        allocation4 = new byte[5 * _1MB]; // 5M

        System.out.println("完毕");
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值