• JVM内存管理 + GC垃圾回收机制*


    2、JVM内存管理

    JVM将内存划分为6个部分:PC寄存器(也叫程序计数器)、虚拟机栈、堆、方法区、运行时常量池、本地方法栈

    这里写图片描述

    • PC寄存器(程序计数器):用于记录当前线程运行时的位置,每一个线程都有一个独立的程序计数器,线程的阻塞、恢复、挂起等一系列操作都需要程序计数器的参与,因此必须是线程私有的。

    • java 虚拟机栈:在创建线程时创建的,用来存储栈帧,因此也是线程私有的。java程序中的方法在执行时,会创建一个栈帧,用于存储方法运行时的临时数据和中间结果,包括局部变量表、操作数栈、动态链接、方法出口等信息。这些栈帧就存储在栈中。如果栈深度大于虚拟机允许的最大深度,则抛出StackOverflowError异常。

      • 局部变量表:方法的局部变量列表,在编译时就写入了class文件
      • 操作数栈:int x = 1; 就需要将 1 压入操作数栈,再将 1 赋值给变量x
    • java 堆:java堆被所有线程共享,堆的主要作用就是存储对象。如果堆空间不够,但扩展时又不能申请到足够的内存时,则抛出OutOfMemoryError异常。

      StackOverflowErrorOutOfMemoryError
      java栈 java堆
      栈深度超过范围了(比如:递归层数太多了) 内存空间不够了(需要及时释放内存)
    • 方法区:方发区被各个线程共享,用于存储静态变量、运行时常量池等信息。

    • 本地方法栈:本地方法栈的主要作用就是支持native方法,比如在java中调用C/C++

    3、GC回收机制

    • 哪些内存需要回收?——who
    • 什么时候回收?——when
    • 怎么回收?——how

    1、哪些内存需要回收?

    • java堆、方法区的内存
    线程私有线程共享
    程序计数器、虚拟机栈、本地方法栈 java堆、方法区
    随线程生而生,随线程去而去。线程分配多少内存都是有数的,当线程销毁时,内存就被释放了 堆和方法区的内存都是动态分配的(使用new关键字),所以也需要动态回收。
    这部分内存的回收依赖GC完成

    2、什么时候回收?

    • 引用计数法
    • 可达性分析

    (1)、引用计数法

    给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反之每当一个引用失效时,计数器减一。当计数器为0时,则表示对象不被引用。

    举个例子:

    Object a = new Object(); // a的引用计数为1
    a = null; // a的引用计数为0,等待GC回收
    • 1
    • 2

    但是,引用计数法不能解决对象之间的循环引用,见下例

    Object a = new Object(); // a的引用计数为1
    Object b = new Object(); // b的引用计数为1
    
    a.next = b; // a的引用计数为2
    b.next = a; // b的引用计数为2
    
    a = null; // a的引用计数为1,尽管已经显示地将a赋值为null,但是由于引用计数为1,GC无法回收a
    b = null; // b的引用计数为1,同理,GC也不回收b
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (2)、可达性分析

    设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找不到根时,就认为该对象不可达。

    这里写图片描述

    没有一条从根到Object4 和 Object5的路径,说明这两个对象到根是不可达的,可以被回收

    补充:java中,可以作为GC Roots的对象包括:

    • java虚拟机栈中引用的对象
    • 方法区中静态变量引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中引用的对象

    3、怎么回收?

    • 标记——清除算法
    • 复制算法
    • 分代算法

    (1)、标记——清除算法

    遍历所有的GC Root,分别标记处可达的对象和不可达的对象,然后将不可达的对象回收。

    缺点是:效率低、回收得到的空间不连续

    (2)、复制算法

    将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回收。

    优点是:能够得到连续的内存空间 
    缺点是:浪费了一半内存

    这里写图片描述

    (3)、分代算法

    在java中,把内存中的对象按生命长短分为:

    • 新生代:活不了多久就go die 了,比如局部变量
    • 老年代:老不死的,活的久但也会go die,比如一些生命周期长的对象
    • 永久代:千年王八万年龟,不死,比如加载的class信息

    有一点需要注意:新生代和老年代存储在java虚拟机堆上 ;永久代存储在方法区上

    回收方法
    新生代 使用复制算法
    老年代 使用标记——清除算法
    永久代 ————————

    补充:java finalize()方法:

    在被GC回收前,可以做一些操作,比如释放资源。有点像析构函数,但是一个对象只能调用一次finalize()方法。

  • 相关阅读:
    IDEA Inspections详解
    IDEA 阿里编码规范插件
    IDEA Save Actions插件
    IDEA Lombok插件
    设计模式-简单工厂模式与工厂模式
    DAO设计模式
    Jcrop+strut2+jsp实现图片剪切
    Spring的Bean的作用域
    Spring的事务机制
    windows 下 Redis 主从 读写分离
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/9666924.html
Copyright © 2020-2023  润新知