zoukankan      html  css  js  c++  java
  • Java 的内存模型、垃圾回收机制和四种引用方式

    一、内存模型

    Java中的内存分为五个部分,分别是方法区、虚拟机栈、堆、程序计数器和本地方法区。

    1.方法区

    存放运行时加载的类信息、静态变量、常量等信息。

    2.虚拟机栈

    存放对象的引用、方法的返回地址等。
    每个线程都有一个栈。

    3.堆

    主要存放对象的实例。
    所有线程共享同一个堆。

    4.程序计数器

    存放运行程序的行数信息。

    5.本地方法区

    和方法区类似,但存放的native方法的相关内容。

    二、垃圾回收

    1.垃圾回收的对象

    清理的内容主要是存在于堆和方法区的内容。

    2.清理方法

    方法一:引用计数:当一个对象被引用次数+1,引用释放时次数-1,=0时即可回收。但是存在互相引用问题,会导致无法回收。
    方法二:可达性分析:当一个对象没有通过引用链和GC roots相连的时候,就表示已经没有用,可以回收了。
    GC roots有哪些?
    - 虚拟机(栈帧中的本地变量表)中引用的对象
    - 方法区中类静态属性引用的对象
    - 方法区中常量引用的对象
    - 本地方法栈中JNI(即一般说的native方法)中引用的对象

    3.清理分类和触发条件

    1. MINOR GC
      eden满的时候触发。
    2. FULL GC
      调用System.gc()会被建议使用full;
      老年区空间不足;
      方法区空间不足;
      MINOR GC方法空间无法满足

    4.垃圾回收算法

    1. 标记-清除法
      • 做法:
        当发现需要回收的内容时,标记并直接清除。
      • 缺点:
        需要遍历全堆,复杂度高,并且会带来内存碎片等问题
    2. 标记-整理法
      • 做法:
        将不需要回收的内容标记并整理到一起,然后清除剩下的部分
      • 优缺点:
        没有内存碎片问题;但是如果存活的多,会产生多次搬运,降低效率。
    3. 复制法
      • 做法:
        将内存分两块,每次将存活的对象搬运到另一块,并清理这一块。
      • 优缺点
        清理容易,但是内存利用率低。
    4. 分代收集法
      • 理论:
        新创建的对象一般都是生命周期较短的,分块可以优先回收这些。
      • 做法:
        1. 内存分年轻代和年老代,年轻代再分为3区,eden区和a区、b区。新建的对象放在年轻代的eden区;
        2. 当eden区满了,对eden进行gc,把存活的搬入a区
        3. 当eden再次满了,对eden和a区进行gc,将eden区和a区的存活的搬入b区;
        4. 重复2、3步,直到一定次数后,将仍然存活、在a、b区反复搬运的内容搬入年老区;
        5. 年老区满了以后,进行全量gc,即对所有的区都做gc,是最慢的。
      • 优缺点
        挺好的,现在都用分代收集法。

    5.垃圾回收的实现

    1. 串行回收
    2. 并行回收
    3. 与用户线程同存的回收
    4. G1回收
    

    三、引用方式

    强引用(FinalReference)

    即使OOM也不会被清理。对对象的引用默认就是这种方式。例如,Object o = new Object();
    【FinalReference类不是public的,没办法直接用】
    

    软引用(SoftReference)

    当内存不足的时候会被清理。比较适用于缓存。
    

    弱引用(WeakReference)

    当没有再被引用时,随时都有可能被清理。
    这里写一个小例子来展示弱引用的效果。
        public static void main(String[] args) throws InterruptedException {
            AtomicInteger integer = new AtomicInteger(12);
            WeakReference<AtomicInteger> reference = new WeakReference<>(integer);
            System.out.println("正在使用时,reference.get()="+reference.get());
            System.gc();
            System.out.println("引用的对象还引用着,reference.get()="+reference.get());
            integer = null;
            System.out.println("引用的对象置空了,但是还未进行垃圾回收,reference.get()="+reference.get());
            System.gc();
            System.out.println("引用的对象置空了,垃圾回收后,reference.get()="+reference.get());
        }
    运行结果:
        正在使用时,reference.get()=12
        引用的对象还引用着,reference.get()=12
        引用的对象置空了,但是还未进行垃圾回收,reference.get()=12
        引用的对象置空了,垃圾回收后,reference.get()=null
    可以看到:
        被弱引用的对象在还没有结束生命周期时,即使gc也不会被清理;
        当将被弱引用的对象生命周期结束(=null)时,立即去引用里get(),仍然可以拿到,证明了这一刻还未被回收;
        再次调用gc(),这时再get()就已经没有了。
    
    如果把WeakReference换成SoftReference,那么最后一次get的时候就不会是null。
    

    虚引用(PhantomReference)

    类似于弱引用,但是在被即将收集时,会被放入一个引用队列。用户可以查询引用队列看是否该对象会被清理,进行一些额外的操作。
  • 相关阅读:
    PAT 甲级 1027 Colors in Mars
    PAT 甲级 1026 Table Tennis(模拟)
    PAT 甲级 1025 PAT Ranking
    PAT 甲级 1024 Palindromic Number
    PAT 甲级 1023 Have Fun with Numbers
    PAT 甲级 1021 Deepest Root (并查集,树的遍历)
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯 算法提高 抽卡游戏
    Java实现 蓝桥杯 算法提高 抽卡游戏
  • 原文地址:https://www.cnblogs.com/pravez/p/12519992.html
Copyright © 2011-2022 走看看