icode icode
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

iqqcode

保持对技术的探索实践与热爱
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaSE

    • 面向对象

    • 常用API

    • 集合类

    • 多线程

    • JDK8新特性

    • IO流

    • JVM

      • 类加载系统
      • 运行时数据区及线程
      • 程序计数器
      • 虚拟机栈
      • 本地方法栈
      • 堆
      • 方法区
      • 对象实例化的内存布局与访问定位
      • 面试考点
      • 垃圾回收算法
      • 执行引擎
      • 垃圾回收相关概念
        • 1. System.gc()
        • 2. 内存溢出
          • OOM调试
        • 3. 内存泄漏
        • 4. 常用的JVM参数
        • 5. 再谈引用
      • 垃圾回收算法
      • 垃圾回收器
      • GC日志分析
      • JVM内存区域与内存溢出异常
      • JVM垃圾回收器与内存分配策略
      • Java内存模型JMM详解
      • StringTable详解
      • 站在虚拟机栈的角度,用字节码来理解i++和++i
  • JavaWeb

  • JavaEE

  • JavaTopic

  • 设计模式

  • 计算机基础

  • Java后端
  • JavaSE
  • JVM
iqqcode
2021-06-17
目录

垃圾回收相关概念

4 laptops with black background

# 1. System.gc()

在默认情况下,通过System.gc()或者 Runtime. getRuntime().gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。

然而System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用

只是提醒JVM进行GC,不保证JVM一定会GC。类似于线程的优先级

System.gc()不一定会立即进行垃圾回收:

public class SystemCGTest {
    public static void main(String[] args) {
        new SystemCGTest();
        //提醒JVM的垃级回收器执行gc,但是不确定是否马上执行gc
        System.gc();
    }

    @Override
    protected void finalize() {
        System.out.println("重写finalize方法,对象自我拯救...");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

不同的结果出现:

gc()的底层实现是

 Runtime.getRuntime().gc();
1

System.runFinalization会强制立即进行垃圾回收:

public class SystemCGTest {
    public static void main(String[] args) {
        new SystemCGTest();
        //提醒JVM的垃级回收器执行gc,但是不确定是否马上执行gc
        System.gc();

        //调用finalize(),强制GC立即进行垃圾回收
        System.runFinalization();
    }

    @Override
    protected void finalize() {
        System.out.println("重写finalize方法,对象自我拯救...");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

只会出现:重写finalize方法,对象自我拯救...这一种结果

runFinalization()会强制调用引用的对象的finalize()方法,而只在回收垃圾对象时GC才会调用finalize(),所以推导得出runFinalization()会使JVM立即进行垃圾回收


JVM实现者可以通过System.gc()调用来决定JVM的GC行为。而一般情况下,垃圾回收应该是自动进行的,无须手动触发。

在些特殊情况下,如我们正在编写一个性能基准测试,保证起始点和空间够用,可以在运行之间调用System. gc()


# 2. 内存溢出

javadoc中对OutofMemoryError的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。

1. 没有空闲内存

造成没有空闲内存的原因有二:

  1. 堆内存设置不够-Xms、-Xmx来调整

  2. 代码中创建了大对象,长时间存在引用而不能被垃圾回收器回收(内存泄漏)

永久代:java.lang.OutOfMemoryError:PermGen space

元空间:java.lang.OutOfMemoryError:Metaspace

2. 无法提供更多内存

意思就是GC回收了垃圾对象之后,仍然没有足够的内存空间来存储。GC完之后,才报OOM

【OOM举例】

# OOM调试

【参数设置】

  • -Xms8m(设置堆空间)

  • -Xmx8m

  • -XX:+HeapDumpOnOutOfMemoryError(OOM时导出Dump文件,进行分析)

/**
 * @Author: Mr.Q
 * @Date: 2020-08-05 11:58
 * @Description:堆空间OOM测试,dump快照分析
 *
 * -Xms8m
 * -Xmx8m
 * -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOOM {

    byte[] bufffer = new byte[1 * 1024 * 1024]; //1MB

    public static void main(String[] args) {
        ArrayList<HeapOOM> list = new ArrayList<HeapOOM> ();
        int count = 0;
        try {
            while(true) {
                list.add(new HeapOOM());
                count++;
            }
        } catch (Throwable t) {
            System.out.println("count = " + count);
            t.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

生成Dump文件,查看内存情况

# 3. 内存泄漏

内存泄漏Memory Leak:对象不再使用了,但是GC又无法将其回收

尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现 OutofMemory异常,导致程序崩溃。

内存泄漏可能导致内存溢出,并不绝对。

注意,这里的存储空间并不是指物理内存,而是指JVM虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。

【Memory Leak举例】

  1. 单例模式。单例的生命周期和类是一样长的,因为是static 类型。所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。

  2. 一些提供close的资源未关闭,导致内存泄漏。数据库连接、网络连接Socket和 IO 连接必须手动close,否则是不能被回收的。


# 4. 常用的JVM参数

-Xss2M :设置JVM栈内存大小

-Xms20M:设置堆内存初始值

-Xmx20M:设置堆内存最大值

-Xmn10M:设置堆内存中新生代大小

-XX:SurvivorRatio=8:设置堆内存中新生代Eden 和 Survivor 比例

-XX:PermSize=10M:设置方法区内存初始值

-XX:MaxPermSize=10M:设置方法区内存最大值

-XX:MaxDirectMemorySize=10M:设置堆内存中新生代大小


# 5. 再谈引用

【面试题】:强引用、软引用、弱引用、虚引用有什么区别?具体使场景是什么?

我们希望能描述这样一类对象:

  • 当内存空间还足够时,则能保留在内存中;

  • 如果内存空间在进行垃圾收集后还是很紧张,则可以抛弃这些对象。

在JDK1.2版之后,Java对引用的概念进行了扩充,将引用分为

  • 强引用( Strong Reference)

  • 软引用( Soft Reference)

  • 弱引用( Weak Reference)

  • 虚引用( Phantom Reference)

4种,这种引用强度依次逐渐减弱

强引用( StrongReference):传统定义的“引用”,即Object obj = new Object()这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被强引用的对象。

软引用( SoftReference):在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果回收后还没有足够的内存,才会抛出内存溢出异常。没有足够的内存空间才会回收

弱引用( WeakReference):被弱引用关联的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论内存空间是否足够,都会回收掉被弱引用关联的对象。

虚引用( PhantomReference):一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对象的实例。为一个对象设置虚引用关联的唯一目的就是——能在这个对象被收集器回收时收到一个系统通知,进行对象回收跟踪。

编辑 (opens new window)
上次更新: 2021/06/27, 10:49:09
执行引擎
垃圾回收算法

← 执行引擎 垃圾回收算法→

最近更新
01
匿名内部类
10-08
02
函数式接口
10-08
03
ARouter-Kotlin踩坑
10-05
更多文章>
Theme by Vdoing | Copyright © 2021-2023 iqqcode | MIT License | 备案号-京ICP备2021028793号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×