JAVA-JVM初步学习笔记

本文概述了JVM的基础概念,包括其位置、体系结构、类加载器机制、双亲委派、沙箱安全、Native关键字、内存区域与GC策略,以及Java8新变化。重点讲解了内存溢出分析、JVM参数设置和内存快照分析方法。
摘要由CSDN通过智能技术生成

JVM初步学习

需要掌握的问题

  • 请你谈谈你对JVM的理解?java8的虚拟机和之前的变化更新?

  • 什么是OOM,什么是栈溢出StackOverFlowError?怎么进行分析?

  • JVM的常用钓友参数有哪些?

  • 内存快照如何抓取,怎么分析Dump文件?知道吗?

  • 谈谈JVM中,类加载器你的认识?

1、JVM位置

jvm 在操作系统之上,运行在操作系统上! JVM使用C语言编写的。 包含在JRE里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UthA0APG-1631797521477)(C:\Users\10238\Desktop\狂神笔记\Untitled.assets\image-20201107140206942.png)]

2、JVM的体系结构

如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YO8BgdVt-1631797521482)(C:\Users\10238\Desktop\狂神笔记\Untitled.assets\image-20201107142520583.png)]

方法区:MethodArea

Java栈 Stack 不会有垃圾回收

本地方法栈 Native Method Stack 不会有垃圾回收

堆 Head 会有垃圾回收

程序计数器 不会有垃圾回收

执行引擎

本地方法接口

本地方法库

JVM调优99%都是在方法区和堆里面进行调优,都是在调整堆

JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register(程序计数器) , VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的),Native Method Stack ( 本地方法栈 ),其中Method Area 和 Heap 是线程共享的 ,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享。为什么分为 线程共享和非线程共享的呢

3、类加载器

​ 作用:加载class文件~ new Student();

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4Hc66Tk-1631797521486)(C:\Users\10238\Desktop\狂神笔记\Untitled.assets\image-20201107203720753.png)]

类加载器的类别: 1. 虚拟机自带的加载器 2.启动类(根BootstrapClassLoader) 加载器 3.扩展类加载器 ExtensionClassLoader4 应用程序加载器 AppClassLoader或者SystemClassLoader

类加载的时机

1. 创建类的实例,也就是new一个对象,会加载
2. 访问某个类或接口的静态变量,或者对该静态变量赋值
3. 调用类的静态方法
4. 反射(Class.forName("com.lji.load"))
5. 初始化一个类的子类(会首先初始化子类的父类)
6. jvm启动时表明的启动类,既文件名和类名相同的那个类

4、双亲委派机制

为了保证安全

​ APP–>EXC–BOOT(最终执行的是这里)

​ 加载过程:

  1. 类加载器收到类加载的请求, Application

  2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器 BootStrap

  3. 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子加载器进行加载

  4. 重复步骤3,

    ClassNotFount~

    Null: java调用不到~C,C++

    java = c+±-

​ 例如如下代码

package java.lang;

public class String{
    
    //双亲委派机制:安全
    //1. app -->exec-->boot
    public String toString(){
        return "Hello";
    }
    public static void main(String [] args){
        String s = new String();
        s.toString();
    }
    
}
//上面代码无法运行
public class Student{
    
    //双亲委派机制:安全
    //1. app -->exec-->boot
    public String toString(){
        return "Hello";
    }
    public static void main(String [] args){
        Student s = new Student();
        s.toString();
    }
    
}
//这个代码可以运行
最终执行的是根的String, rt.jar包中
 加载一个类的时候回,总是先去根加载器里面找是否有String类,再去ExT加载器里面找是否有String类,最后去APP加载器里面找是否有String.

获取加载器的过程

public class Car {
    public static void main(String[] args) {
        Car car1 = new Car();
        Car car2 = new Car();
        Car car3 = new Car();

        System.out.println(car1.hashCode());
        System.out.println(car2.hashCode());
        System.out.println(car3.hashCode());

        System.out.println(car1.getClass().hashCode());
        System.out.println(car2.getClass().hashCode());
        System.out.println(car3.getClass().hashCode());

        System.out.println(car1.getClass().getClassLoader());
        System.out.println(car1.getClass().getClassLoader().getParent());
        System.out.println(car1.getClass().getClassLoader().getParent().getParent());
    }
    输出:
    1956725890
    356573597
    1735600054
    1163157884
    1163157884
    1163157884
    sun.misc.Launcher$AppClassLoader@18b4aac2
    sun.misc.Launcher$ExtClassLoader@14ae5a5
    null

5、沙箱安全机制

沙箱就是一个限制程序运行的环境,沙箱机制就是将JAVA代码限制在虚拟机特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离。

百度查询

6、Native关键字

//private native void hello();

Java Native Interface JNI

凡是带了Native关键字的方法,说明Java的作用范围是达不到的,需要去调用底层C语言的库! 进入到本地方法栈 (Native Method Stack),调用本地方法接口JNI

本地方法接口(JNI)的作用就是调用本地方法库,扩展JAVA的作用,融合不同的编程语言为JAVA所用! 最初想融合进入c、C++等语言

java诞生的时候回,C、C++程序员很多,想要立足,必须要有调用C、C++的程序,于是在内存中专管开辟了一段Native Method Stack, 本地方法栈! 等级Native方法,最终执行的时候。加载本地方法库中的方法通过JNI。比如JAVA程序使用打印机,JAVA程序使用摄像头,JAVA程序使用机器人等,等写JAVA用到大量本地方法,会用到JNI,在企业级应用中较为少见。

现在调用其他接口,现在我们已经开始采用了 Socket, WebService…https等

7、PC寄存器

程序计数器 Program Counter Register

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向每一条指令的地址,也即将要执行的指令代码)。在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

8、方法区

Method Area方法区

方法区是被所有线程共享,所有的字段和方法字节码,以及一些特殊方法,如构造函数、接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,该区域属于共享区间。

static静态变量、final常量、类信息(构造方法、 接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

​ static int demo =10;

​ final int DEMO = 20

​ Class 常量池

9、栈 STACK

栈 ,是一种数据结构,程序 = 数据结构+算法,持续学习的东西

程序 = 框架+业务逻辑~ 吃饭,持续学习! SprintBoot+Sprint Spring 吃饭用的

栈的特点: 先进后出,后进先出,桶

队列: 先进先出(FIFO First Input First Output) 喝多了吐,就是栈,吃多了拉,就是队列

为什么main()先执行,却最后结束~

栈: 栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存就会被释放;栈内存结束了,程序也就结束了,对于栈来说,不存在垃圾回收问题

栈 :8大基本类型+对象引用+实例方法,是存放在栈里面的;

栈运行原理:

​ 以栈帧的形式存在,程序正在执行的方法,一定在栈的顶部~ StackOverFlowError

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JP5zhW6e-1631797521489)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108192533763.png)]

画出一个对象在内存实例化的过程。

public class People {
    String name; //定义一个成员变量 name
    int age; //成员变量age
    Double height; //成员变量

    void sing(){
        System.out.println("人的姓名" + name);
        System.out.println("人的年龄" + age);
        System.out.println("人的身高" + height);
    }

    public static void main(String[] args) {
        String name; //定义一个局部变量 name
        int age; //局部变量age
        Double height; //局部变量

        People people = new People();//实例化对象people
        people.name ="张三";//赋值
        people.age =18;//赋值
        people.height =180.85; //赋值
        people.sing();
    }
}

  1. 首先, 在程序的执行过程中,首先类中的成员变量和方法体会进入到方法区,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnZ33rkb-1631797521491)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108222235152.png)]

  1. 程序执行到main()方法时,main()函数方法体会进入栈区,这一过程叫做进栈,定义了一个指向Person的实例变量person

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBj497Vo-1631797521494)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108222524229.png)]

  1. 当程序执行到Person person = new Person(),就会在堆内存开辟一块内存区间,用于存放Person实例对象,然后将成员变量和成员方法放在new实例中,都是取成员变量&成员方法的地址值,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeqgTPDM-1631797521495)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108223935501.png)]

  1. 接下来对 person 对象进行赋值, person.name = “小二” ; perison.age = 13; person.height= 180.0; 先在栈区找到 person,然后根据地址值找到 new Person() 进行赋值操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hs1SKwVM-1631797521496)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108224126912.png)]

5.当程序走到 sing() 方法时,先到栈区找到 person这个引用变量,然后根据该地址值在堆内存中找到 new Person() 进行方法调用。

在方法体void sing()被调用完成后,就会立刻马上从栈内弹出(出站 )

最后,在main()函数完成后,main()函数也会出栈 如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HOdZDKD-1631797521497)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201108224419046.png)]

栈+堆+方法区 交互的一些关系:

10、三种JVM的区别

HotSpot(SUN公司) JRockit (Oracle BEA) J9VM(IBM公司)

我们学习的堆都基HotSpot

堆(Heap) 一个JVM只有一个堆内存,堆内存的大小是可以调节的,类加载器读取了类文件后,会把什么东西放到堆里面呢? 类具体的实例,类的方法、常量、变量,保存我们所有引用类型的真实对象。

栈中只放一些引用,

堆的内存中还要进行细分为三个区域,

  • 新生区 (伊甸园区 Eden Space / Young /New Area) 一般新生区有3个区 [Eden Space 区, Survive 0区, Survive1区 8:1:1]
    • 幸存区 经过垃圾回收还能够生存的对象进入到幸存区,如果对象在幸存区,经过一定数量仍然可以存活,则进入养老区
    • 养老区(Old Area)
    • 永久区

垃圾回收分两种

​ 轻量级-Miror GC 针对新生区

​ 重量级- Full GC 比如养老区也满了,会触发养老区的垃圾回收

GC垃圾回收,主要在Eden Space和 Older Space区。假设内存满了,就会爆出00M(java.lang.OutOfMemoryError:java heap space),堆内存不够的错误。

在JDK8之前,永久存储区有另外一个名字(元空间); JDK8以后,永久存储区变为了 [元空间]

11、新生区、老年区、永久区

​ 新生区,类诞生和成长的地方,甚至死亡;分为伊甸园区和幸存者区(幸存1区,幸存2区), 所有的对象都是在伊甸园区new 出来的!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tF8AsvBA-1631797521498)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109113528123.png)]

真理:经过研究,99%的对象都是临时对象!

永久区: 这个区域常驻内存,用来存放JDK自身携带的Class对象,包括一些Interface元数据,存放的是我们java运行时的一些环境或者类信息,这个区域不存在垃圾回收! 关闭jvm虚拟机就会释放这个区域的内存~ 出现一些OOM的错误,和JVM也没关系

当一个启动类,加载了大量的第三方jar包。tomcat部署了太多的应用,这样容易导致永久区空间OO,或者大量的动态生成的反射类,如果这些东西不断的被加载,直到内存满,就会出现OOM错误

逻辑上存在,物理上不存在 --元空间

- jdk 1.6之前  永久代,常量池在方法区中
- jdk 1.7       永久代,但是慢慢退化了,提出了去永久代概念,常量池在堆中
- jdk 1.8        无永久代,变为元空间,常量池放在了元空间
public class MaxJVMDemo {
    public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        //返回jvm的总内存
        long totalMemory = Runtime.getRuntime().totalMemory();

        System.out.println("maxMemory=" + maxMemory + "字节\t" + (maxMemory / (double) 1014 / 1014) + "MB");
        System.out.println("totalMemory=" + totalMemory + "字节\t" + (totalMemory / (double) 1014 / 1014) + "MB");
        /*
        系统7.7  max和系统,约等于1/4   totalMemory  约等于1/64  
        结论: 默认情况下,jvm分配的总内存约是电脑内存的1/4   初始化的total内存,约是电脑的1/64
         */
    }
}

堆里面抽象逻辑关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wqEofFTl-1631797521498)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109120104618.png)]

结论: 默认情况下,jvm分配的总内存约是电脑内存的1/4 初始化的total内存,约是电脑的1/64

堆内存调优

我们在pycharm里面可以点击项目,—调出来Run/Debug Configurations,在里面配置Vm选项,-Xms1024m -Xmx1024m -XX:+PrintGCDetails 如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j52RqOyg-1631797521499)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109120328550.png)]

输出结果:

maxMemory=1029177344字节 1000.954432812421MB
totalMemory=1029177344字节 1000.954432812421MB
Heap
PSYoungGen total 305664K, used 15729K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
eden space 262144K, 6% used [0x00000000eab00000,0x00000000eba5c420,0x00000000fab00000)
from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
to space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
ParOldGen total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
Metaspace used 3230K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 353K, capacity 388K, committed 512K, reserved 1048576K

示例代码演示OOM错误及垃圾清理过程:

// -Xms8m -Xmx8m -XX:+PrintGCDetails
public class TestOutOfMemoryDemo {
    public static void main(String[] args) {
        String str = "kuangshensayjava";
        while (true){
            str += str + new Random().nextInt(888888888)+new Random().nextInt(99999999);
        }
    }
}


输出:

[GC (Allocation Failure) [PSYoungGen: 1536K->504K(2048K)] 1536K->648K(7680K), 0.0007097 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1828K->499K(2048K)] 1972K->1155K(7680K), 0.0005263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1857K->464K(2048K)] 2513K->1910K(7680K), 0.0006237 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1600K->496K(2048K)] 4100K->3523K(7680K), 0.0006266 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1076K->480K(2048K)] 5157K->4577K(7680K), 0.0004294 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 480K->480K(2048K)] 4577K->4585K(7680K), 0.0003617 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 480K->0K(2048K)] [ParOldGen: 4105K->2202K(5632K)] 4585K->2202K(7680K), [Metaspace: 3220K->3220K(1056768K)], 0.0036330 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 1121K->0K(2048K)] [ParOldGen: 5364K->2704K(5632K)] 6486K->2704K(7680K), [Metaspace: 3272K->3272K(1056768K)], 0.0047269 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 68K->160K(2048K)] 4881K->4973K(7680K), 0.0004437 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 160K->0K(2048K)] [ParOldGen: 4813K->3760K(5632K)] 4973K->3760K(7680K), [Metaspace: 3280K->3280K(1056768K)], 0.0051718 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 3760K->3760K(7680K), 0.0002537 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 3760K->3740K(5632K)] 3760K->3740K(7680K), [Metaspace: 3280K->3280K(1056768K)], 0.0045350 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 2048K, used 53K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000)
  eden space 1536K, 3% used [0x00000000ffd80000,0x00000000ffd8d550,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 5632K, used 3740K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000)
  object space 5632K, 66% used [0x00000000ff800000,0x00000000ffba7140,0x00000000ffd80000)
 Metaspace       used 3324K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 361K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
	at java.lang.StringBuilder.append(StringBuilder.java:208)
	at top.aigoo.jvm.TestOutOfMemoryDemo.main(TestOutOfMemoryDemo.java:10)

Process finished with exit code 1

使用Jprofiler分析OOM内存溢出原因

在一个项目中,突然出现了OOM故障,那么该如何排除? 研究为什么出错? 最快的一种方法,能够看到代码第几行出错。最慢的情况是debug一行行分析代码! 所以我们可以使用一些内存快照分析工具,来进行分析! MAT和JProfiler是一样的,都是做内存分析,内存快照的!

JProfiler的好处

  • 分析Dump内存文件,快读定位内存泄漏
  • 获得堆中的数据
  • 获得大的对象~

JProfiler_windows_x64_9_2_1.exe 需要安装这个客户端

Step1.安装步骤

先去官网进行下载这个软件,

注意,安装时候,安装路径不能有空格和中文 如F:\Environment\jprofiler11

Step2 .在idea配置步骤

安装完成后,我们进入IDEA的SETTING进行配置下(前提是我们之前已经在IDEA里面安装了JProfiler的插件),如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j3xvlt64-1631797521500)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109125805041.png)]

step3. 示例代码:

思路,每个对象都申请一个1M的字节数组,然后再main函数中,持续生成实例,直至内存爆出,出现OutOfMemoryError,但是try{}catch…使用了Exception无法捕获到。

public class TestJProfilerDemo {
    byte[] array = new byte[1 * 1024 * 1024];//每个数组1M

    public static void main(String[] args) {
        ArrayList<TestJProfilerDemo> list = new ArrayList<>();

        int count = 0;//计数器,查看第几次出现内存异常
        try {
            while (true) {
                list.add(new TestJProfilerDemo());
                count += 1;
            }

        } catch (Exception e) {
            System.out.println("count-->:" + count);
            e.printStackTrace();
        }

    }
}

step4. dump内存文件:

需要在IDEA配置的vm options: -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

-Xms 设置初始化内存分配大小 1/64 机器内存六十四分之一

-Xmx 设置最大分配内存,默认是1/4

-XX:+ PrintGCDetails 打印内存的垃圾回收信息

-XX:+HeapDumpOnOutOfMemoryError 打印OOM Dump信息

配置好之后,运行程序,我们就可以找到对应的内存分析文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IEkoDIf2-1631797521501)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109130908311.png)]

​ step5. 双击dump内存文件,就可以进入到JProfiler软件

​ 一般分析两个,第一个是CurrentObjectSet里面的 BiggestObjects

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8jMbZPYo-1631797521502)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109131041638.png)]

​ 另一个就是ThreadDump,看具体那个线程哪行代码错误

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQ8CRIxA-1631797521502)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109131049201.png)]

11、GC

JVM在进行GC时,并不是对着三个区域统一回收,大部分时候,回收都在新生代~

幸存区(from ,to )

GC分两种:轻GC(普通的GC) ,重GC(全局GC)

题目:

​ JVM的内存模型和分区,每个分区都放得是什么东西

​ 堆里面有哪些分区?Eden space Survive from,to, 老年区,说说他们的特点

​ GC的算法有哪些? 标记清除法,标记整理/压缩法,复制算法,引用计数器,怎么用的?

​ 轻GC和重GC分别在什么时候发生?

​ 他们的特点和区别。

方法一。引用计数法:

计数器本身也有消耗,如果死循环,就会疯狂引用,这个效率不高!

方法二、复制算法

记住一句话:幸存区from和幸存区to,谁空谁就是to,

1.每次GC都会将Eden区活的对象移动到幸存区,一旦Eden区被GC后,就会都是空的!

2.从from区的2个复制到to区 ,复制算法是为了保证to区是干净的。

3.当对象经历了15次GC都还么有死,这时候就会进入到养老区,这个15次是可以调整, -XX:MaxTenuringThreshold=5 通过这个参数,可以设定进入老年代的时间,默认是15次

好处:没有内存的碎片,每次都是一整个区移动,从to到from,同时切换了from和to区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XbAos35Y-1631797521503)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109150756470.png)]

坏处: 浪费了内存空间~多了一半空间,永远是空,假设对象100%存活,则复制算法的弊端会被放大

复制算法最佳使用场景,对象的存活度较低的时候,复制算法有优势,一半应用在新生区

方法三、标记清除算法

扫描这些对象,回收时候对对象进行标记

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcMrD7ID-1631797521504)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109151140629.png)]

清除 ,对没有标记的对象,进行清除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydbDnHhb-1631797521505)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109151146863.png)]

好处:不需要额外的空间! 当所有的内存都被标记时候,此时就会内存满了,也会爆出来00M

缺点: 两次扫描,严重浪费时间,会产生内存的碎片

方法四、标记压缩算法

在标记清除的基础上,再次进行扫描,进行再次优化! 进行压缩,向一端移动存活的对象,防止内存碎片的产生,但是多了一个移动的成本。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5yKU6ibz-1631797521505)(F:\MD格式学习笔记库\狂神JAVA-7-JVM初步学习.assets\image-20201109151507522.png)]

12、总结

内存效率 赋值算法>标记清除散发>标记压缩算法(时间复杂度)

内存整齐度: 复制算法=标记压缩算法>标记清除算法

内存利用率 标记压缩算法=标记清除算法>复制算法

没有最好的算法,只有最合适的算法! 所以GC也叫作分代收集算法!

年轻代,存活率低,一般使用复制算法!

老年代,存活率高,区域大,一般使用标记清除+标记压缩算法混合实现!

学习方案:

​ 老师提出问题,自己百度

​ 思维导图

单点登录~|SSO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值