【深入理解Java原理】垃圾回收原理

简介: 【深入理解Java原理】垃圾回收原理

CMS垃圾回收器 Concurent Marked Sweep

并行的标记垃圾回收器

获取最短停顿的回收器, 标记清除算法实现

缺点是:

1.对cpu资源敏感

2.无法处理浮动垃圾

3.有大量碎片产生


CMS 垃圾回收的六个步骤:

1. 初始标记

  初始标记会触发 stop the world ,从垃圾回收的根对象开始查找,这个过程会暂停整个JVM,但是很快结束

2.并行标记

    并发标记进行和用户线程同时执行。用户不会感觉卡顿

3.并发预清理

     并发预清理阶段是并行的,标记新生代进入老年代的对象。

4.重新标记

   扫描 堆中剩余的对象,然后重新从根对象进行扫描会 stop the word

5.并发清理

   清理垃圾对象。和用户线程并发执行

6.并发重置

    重置CMS数据结构,等待下一次垃圾回收

640.jpg


什么时候触发GC?


1. 执行 system.gc()的时候

2.老年代空间不足,一次Full GC 之后,然后不足 会触发 java.outofmemoryError:java heap space

3.永久代空间不足    永生代或者永久代java.outofMemory  PerGen Space

4. minor之后 survior放不下,放入老年代,老年代也放不下,触发FullGC, 或者新生代有对象放入老年代,老年代放不下,触发FullGC

5.新生代晋升为老年代时候,老年代剩余空间低于新生代晋升为老年代的速率,会触发老年代回收

6. new 一个大对象,新生代放不下,直接到老年代,空间不够,触发FullGC


怎么避免频繁GC

1. 不要频繁的new 对象

2. 不要显示的调研system.gc()

3. 不要用String+ 使用StringBuilder

4.不要使用Long Integer 尽量使用基本类型

5.少用静态变量 不会回收

6.可以使用null 进行回收

什么是内存泄漏?如何避免?


内存泄露是,这个对象从Root可以找到,但是这个对象是无用的,永远不会被回收,这个就存在内存泄漏


Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null; 
}


为什么JDK1.8之后要移除Permgen Space 替换成了MetaSpace ?


permGen存放的数据内容:类的静态常量,字符串,类的元信息


MetaSpace存放的数据:和PermGen数据类型类似,描述类元数据 class已经被移除


区别是啥?


PermGen存放的是在JVM中的,如果load很多Class 的话,会导致OOM :PermGen error问题

但是MetaSpace是存放在 本地内存空间的,是依赖本地内存空间的大小。


为啥要替换成MetaSpace?


1. Class的大小无法控制,无法设定permGen 的大小,太小了,容易溢出,太大了,JVM内存浪费,也容易导致堆内存可用空间少,导致老年代溢出


2.字符串存放在永久代,容易导致内存溢出


3. 提高了FullGC性能,Metadata 到Metadatapointer之间不用扫描

会有个metaspace threshold, 可能存在的问题是,存在内存泄漏,不断的扩展metaspace 会导致机器内存不足, 所以也需要监控


什么是内存溢出?

程序申请内存的时候,内存没有空间分配了。

什么是守护线程?和非守护线程(用户线程)?区别是啥?

守护线程的作用是为用户线程提供服务便利的,垃圾回收器就是一个守护线程?

非守护线程线程全部结束,守护线程跟者结束。

测试用例:

import java.util.concurrent.TimeUnit;  
public class DaemonThreadTest  
{  
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                Thread childThread = new Thread(new ClildThread());  
                childThread.setDaemon(true);  
                childThread.start();  
                System.out.println("I'm main thread...");  
            }  
        });  
        mainThread.start();  
    }  
}  
class ClildThread implements Runnable  
{  
    @Override 
    public void run()  
    {  
        while(true)  
        {  
            System.out.println("I'm child thread..");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }


将主线程停止工作,守护线程依然执行, 可通过如下设置

childThread.setDaemon(false);

守护线程是当非守护线程,全部结束,则程序结束

import java.util.concurrent.TimeUnit;  
public class DaemonThreadTest  
{  
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                Thread childThread = new Thread(new ClildThread());  
                childThread.setDaemon(true);  
                childThread.start();  
                System.out.println("I'm main thread...");  
            }  
        });  
        mainThread.start();  
        Thread otherThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                while(true)  
                {  
                    System.out.println("I'm other user thread...");  
                    try 
                    {  
                        TimeUnit.MILLISECONDS.sleep(1000);  
                    }  
                    catch (InterruptedException e)  
                    {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        });  
        otherThread.start();  
    }  
}  
class ClildThread implements Runnable  
{  
    @Override 
    public void run()  
    {  
        while(true)  
        {  
            System.out.println("I'm child thread..");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
}

哪些内存需要回收 (GC)?

Eden 区域 MinorGc

Old Generation Major GC

FullGC 清理新生代 老年代,清理整个堆。

如何回收(GC)?

640.jpg


1. 引用计数法

增加一个引用 计数器+1

减少一个引用,计数器-1

计数器为0 表示不被引用

对象相互应用时 ,不能被回收

强引用

软应用 :非必需的对象,内存溢出前回收

弱引用 :非必需引用,强度比软引用更弱

虚引用 :最弱的一种引用

.可达性分析


根节点 GC Root开始,向下搜索,搜索过的路径为引用链, 当一个对象没有任何引用链和他相连,证明该对象不可用



有哪些回收算法?


1. 标记清除算法


标记需要回收的对象,然后统一回收


缺点:效率不高,会造成不连续的内存碎片


2.复制 清理算法,


内存分成两块,将存活的对象复制到另一块上。


缺点,内存使用效率不高


3. 标记压缩清理


标记需要回收的对象


让所有存活的对象向一端移动,压缩到一边去,还有边界外的是要回收的对象清理边界以外的内存


4.分代回收算法


根据对象存活的周期,将不同的内存划分成几块


不同存活周期采用不同的垃圾回收算法



有哪些垃圾回收器?


Serial 收集器


单线程收集器, 想回收的会暂停所有线程


ParNew


并行的 Serial的多线程版本 其他的和Serial 一样


CMS


上面已有介绍


G1


如何查看一个进程假死 ?


使用jstack 到处dump 文件

相关文章
|
19天前
|
存储 Java 关系型数据库
高效连接之道:Java连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
38 5
|
9天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
9天前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
11天前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
Java ArrayList扩容的原理
|
9天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
30 6
|
17天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
34 2
|
20天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
17天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
31 1
|
21天前
|
监控 算法 Java
深入理解Java的垃圾回收机制
【10月更文挑战第22天】在Java的世界里,有一个默默无闻却至关重要的角色——垃圾回收(Garbage Collection, GC)。就像城市的清洁工一样,它默默地清理着不再使用的内存空间,确保我们的程序运行得既高效又稳定。但你真的了解垃圾回收是如何工作的吗?让我们一起探索这个看似简单却充满奥秘的过程,看看它是如何影响你的Java应用性能的。
|
23天前
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。