Java内存管理 -JVM 垃圾回收

简介: 一.概述 相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存。 在之前的JVM 之内存管理 中,我们介绍了JVM内存管理的几个区域,其中程序计数器以及虚拟机栈是线程私有的,随线程而灭,故而它是不用考虑垃圾回收的,因为线程结束其内存空间即释放。

一.概述

相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存。

在之前的JVM 之内存管理 中,我们介绍了JVM内存管理的几个区域,其中程序计数器以及虚拟机栈是线程私有的,随线程而灭,故而它是不用考虑垃圾回收的,因为线程结束其内存空间即释放。

而JAVA堆和方法区则不一样,JAVA堆和方法区时存放的是对象的实例信息以及对象的其他信息,这部分是垃圾回收的主要地点。

二.JAVA堆垃圾回收

垃圾回收主要考虑的问题有两个:一个是效率问题,一个是空间碎片问题。

而Java堆中的垃圾回收可以分为两个区域,一个是新生代,一个是老年代。其中新生代又分为一块比较大的Eden空间和两块较小的Survivor空间。因为新生代和老年代所存储的对象群体是不一样的,为了在效率和空间碎片问题中取得平衡,新生代和老年代所使用的垃圾回收算法是不一样。

新生代 -复制算法

从名字上就知道,新生代主要存放的是比较新的对象,回收多次之后仍然存活的对象,就会被送到老年代中区。由此可知新生代的垃圾回收是比较频繁的,所以为解决效率问题,新生代使用了复制算法。复制算法可以将内存分为大小相等的两块,每次分配时使用其中一块,当这一块用完时,就将还存活的对象复制到另一块内存上面区。此时已使用过的这一块内存就可以一次清理掉,这样也不用担心内存碎片的问题。当然这种算法的一个缺点就是内存使用率比较低,只有一半(每次只能一半用来分配出去)。

而IBM公司的研究表明,新生代中的对象98%都是”照生夕死“,所以不需要按照1:1划分,故而会将内存分为一块较大的Eden空间和两块小的Survivor空间。

那么为什么会有两块Survivor呢,复制算法不是只需要一块Eden和一块Survivor就够了吗?

其实这主要还是为了解决碎片化的问题。假设只有一个Survivor区,当Eden区满的时候,进行Gc,存活对象被分配到了Survivor区,清空Eden区。当再一次Gc完成后,存活的对象继续放在Survivor区,这样不是很美好吗,不会有内存碎片啊!但是别忘了,第一次存到Survivor区的对象很可能在第二次Gc的时候就失活了,清理掉Survivor失活对象不就会产生内存碎片了吗?

所以Java堆使用了两个Survivor区,一个from Survivro和一个toSurvivor,第一次Eden满的时候,复制算法将存活对象放到from Survivor区,清空Eden。第二次,Eden满时,将Eden和from Survivor区存活的对象放到to Survivor区,清空Eden和from Survivor,然后重要的一步,将from Survivor和to Survivor角色互换!这样就解决了内存碎片化的问题。
img_21579aefb6dd117e61535948a1f19854.png

老年代 -标记/整理算法

首先要明白老年代存放的都是会存活得比较久的对象,所以如果老年代也使用复制算法的话,那么复制对象的开销时比较大的,因为老年代的对象基本上都会存活。

标记/整理算法很好理解,主要也就是”标记“,”整理“两个步骤,先将要回收的对象标记,然后让存活对象向着一端移动,最后将边界以外的内存,然后Gc完成。
img_d05142f333d6cc9a631704e702f1ebf2.png

三.方法区垃圾回收

在某些地方的解释中,方法区也会被叫做“永久代”,与JAVA堆不同,这里存放的是类的信息以及一些常量信息,故而这个区域中被分配的内存一般比较难以被回收,所以才有有”永久代“之名。

虽然方法区中垃圾回收效率较低,但被分配的内存却也并非真的就永不被回收,其主要回收的有两部分内容:废弃常量和无用的类。废弃常量的回收与JAVA堆中类实例回收类似,当常量池中一个常量没有被引用时,就有可能被回收。比如常量池中有一个字符串常量“abc”,当没有任何一个String对象值为"abc"时,那么下一次垃圾回收"abc"常量就有可能会被回收。

而对于无用的类的回收,首先需要判断什么样的类才是”无用的类“:

  • 该类所有的实例都已被回收,即JAVA堆中没有该类的实例。
  • 加载类的ClassLoader已经被回收。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可能会堆满足这三个条件的”无用的类“进行回收,仅仅是可能,并非必然。






相关文章
|
13天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
6天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
8天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
29 6
|
12天前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
33 2
|
13天前
|
存储 安全 Java
什么是 Java 的内存模型?
Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)规范的一部分,它定义了一套规则,用于指导Java程序中变量的访问和内存交互方式。
35 1
|
20天前
|
监控 算法 Java
深入理解Java的垃圾回收机制
【10月更文挑战第22天】在Java的世界里,有一个默默无闻却至关重要的角色——垃圾回收(Garbage Collection, GC)。就像城市的清洁工一样,它默默地清理着不再使用的内存空间,确保我们的程序运行得既高效又稳定。但你真的了解垃圾回收是如何工作的吗?让我们一起探索这个看似简单却充满奥秘的过程,看看它是如何影响你的Java应用性能的。
|
19天前
|
存储 运维 Java
💻Java零基础:深入了解Java内存机制
【10月更文挑战第18天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 1
|
22天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
22天前
|
监控 安全 Java
Java Z 垃圾收集器如何彻底改变内存管理
大家好,我是V哥。今天聊聊Java的ZGC(Z Garbage Collector)。ZGC是一个低延迟垃圾收集器,专为大内存应用场景设计。其核心优势包括:极低的暂停时间(通常低于10毫秒)、支持TB级内存、使用着色指针实现高效对象管理、并发压缩和去碎片化、不分代的内存管理。适用于实时数据分析、高性能服务器和在线交易系统等场景,能显著提升应用的性能和稳定性。如何启用?只需在JVM启动参数中加入`-XX:+UseZGC`即可。
144 0
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。