内存管理 Java
⚡ 智能摘要
内存管理 Java 解释了 JVM 如何将运行时内存划分到栈、堆中。 Code以及静态区域,对象引用在方法调用期间如何流动,以及垃圾回收器如何回收不可达对象,从而使应用程序保持稳定且无内存泄漏。
什么是堆栈内存? Java?
堆栈内存 Java 栈是 JVM 内存中存储每个线程的方法帧、局部变量和引用变量的区域。栈始终按照后进先出 (LIFO) 的顺序访问,因此最近调用的方法位于栈顶,其局部变量会随方法帧一起被压入和弹出。
每根线 Java 虚拟机拥有自己的栈,其设计旨在确保方法调用的隔离性和线程安全性。诸如 int、boolean 和 double 之类的基本局部变量直接存在于帧内,而存储在栈上的对象引用则指向堆上分配的对象。
什么是堆内存? Java?
堆内存是 JVM 共享的区域,用于保存所有内存。 Java 使用 `new` 运算符创建的对象和数组,以及作为实例字段属于这些对象的任何引用变量。与栈不同,堆在所有线程之间共享,因此对象访问通常需要同步。
堆是垃圾回收器管理的区域。现代 HotSpot JVM 将堆划分为用于存放生命周期短的对象的年轻区和用于存放生命周期长的对象的老区,类元数据则存储在一个名为元空间的独立本地区域中。
内存分配 Java
内存分配 Java 内存分配是指 JVM 在程序执行期间为变量、类实例和结构体实例分配虚拟内存区域的过程。内存并非在声明对象时分配,而只是创建引用。实际的对象分配是通过 `new` 运算符完成的,因此每个对象都存在于堆上。
此 Java 内存分配分为以下几个部分:
- 堆
- 堆
- Code
- 静止
这种内存划分对于有效管理运行时环境是必要的。
- 此 Code 本节包含您编译的内容。 字节码.
- 此 堆 百货商店 方法、局部变量和参考变量.
- 此 堆 部分包含 对象 也可能包含作为实例字段保存的引用变量。
- 此 静止 本节包含 静态数据和静态方法 所有实例共享。
局部变量和实例变量之间的区别
了解每种变量在 JVM 中的位置有助于解释栈和堆的行为。 实例变量 被宣布 在类内部,但在任何方法之外它作为对象的一部分存在于堆中。
class Student{ int num; // num is instance variable public void showData{}
A 局部变量 被宣布 方法内部,包括方法参数并且存在于活动帧内的堆栈中。
public void sum(int a){ int x = a + 3; // a, x are local variables }
栈和堆的区别
栈和堆在 JVM 中分别解决不同的问题。栈为每个线程提供快速、确定性的内存分配,用于存储与方法作用域相关的短生命周期数据;而堆则提供了一个共享区域,用于存储任何线程都可以访问的长生命周期对象。下面的短视频概述了二者的区别,为接下来的详细讲解做准备。
点击 开始 如果视频无法访问
为了了解栈和堆是如何协同工作的,考虑一个调用方法 m1 的主方法。
public void m1{ int x = 20; }
在 JVM 堆栈中,为方法 m1 创建一个帧。
m1 中的变量 x 也是在堆栈上 m1 的帧中创建的,如下图所示。
方法 m1 然后调用方法 m2。在堆栈中,在 m1 的帧之上为 m2 创建一个新帧。
局部变量 b 和 c 也在栈上 m2 的帧内创建。
public void m2(int b){ boolean c; }
接下来,m2 调用方法 m3。同样,在堆栈顶部创建一个 m3 的帧,如下所示。
假设方法 m3 为 Account 类创建了一个对象,该对象有两个 实例变量 int p 和 int q。
class Account { int p; int q; }
以下是方法 m3 的代码。
public void m3(){ Account ref = new Account(); // more code }
语句 new Account() 在堆上创建一个 Account 对象。
引用变量 ref 在 m3 的帧内的堆栈上创建。
赋值运算符使引用变量指向堆上的对象。
方法执行完毕后,控制权将返回给调用方法,在本例中为方法 m2。
方法 m3 的帧从堆栈中刷新出来。
由于引用变量不再指向堆上的对象,因此该对象符合垃圾回收的条件。
方法 m2 执行完毕后,会从堆栈中弹出,其所有变量都将失效。方法 m1 也一样,最终控制权返回到主方法。
如果一个对象持有另一个引用作为其实例变量会怎样?
public static void main(String args[]) { A parent = new A(); // more code } class A { B child = new B(); int e; } class B { int c; int d; }
在这种情况下,引用变量 child 作为 A 对象的一部分存在于堆上,并且指向它自己的 B 对象,如下所示。
什么是垃圾收集 Java?
垃圾收集 Java 垃圾回收器是 JVM 自动执行内存管理的过程。它会查找任何不再可通过任何活动引用访问的对象,并回收其占用的内存。动态内存分配通过 `new` 运算符实现,并且内存会一直保持分配状态,直到程序不再持有对该对象的任何引用为止。
当所有引用都不存在时,该对象就被认为不再需要,其占用的内存可以被回收。无需显式地销毁对象,因为 Java 通过垃圾回收器自动处理内存释放。
这项技术背后的原理被称为 垃圾收集未能释放内存的程序最终会在没有内存可供分配时崩溃。这类程序被称为内存不足程序。 内存泄漏. 垃圾收集 Java 自动运行 在项目的整个生命周期内,这消除了手动分配的负担,并降低了泄露的风险。
相比之下,在 C 语言中,程序员负责通过 free() 函数释放动态分配的内存。 Java 内存管理具有显著优势。
现代 HotSpot JVM 搭载了多个针对不同工作负载优化的垃圾回收器。默认的 G1 垃圾回收器旨在平衡多 GB 堆的吞吐量和暂停时间。ZGC 和 Shenandoah 则力求在超大堆上实现亚毫秒级的暂停,因此它们在处理大型堆方面表现出色。trac对于延迟敏感型服务而言,选择合适的垃圾回收器并调整新生代、老年代和元空间的大小是一项核心技能。 Java 表演作品。
注意: 所有对象都在内存的堆区创建,该区域由垃圾回收器管理。
示例:学习垃圾收集器机制 Java
本演练演示了在简单程序中,引用何时符合垃圾回收的条件。
步骤1) 将以下代码复制到编辑器中。
class Student{ int a; int b; public void setData(int c,int d){ a=c; b=d; } public void showData(){ System.out.println("Value of a = "+a); System.out.println("Value of b = "+b); } public static void main(String args[]){ Student s1 = new Student(); Student s2 = new Student(); s1.setData(1,2); s2.setData(3,4); s1.showData(); s2.showData(); //Student s3; //s3=s2; //s3.showData(); //s2=null; //s3.showData(); //s3=null; //s3.showData(); } }
步骤2) 保存、编译并运行代码。如图所示,将创建两个对象和两个引用变量。
步骤3) 取消注释第 20、21 和 22 行。保存、编译并运行代码。
步骤4) 如下图所示,两个引用变量现在指向同一个对象。
步骤5) 取消第 23 行和第 24 行的注释。保存、编译并运行代码。
步骤6) 如下所示,s2 变为 null,但 s3 仍然指向该对象,因此该对象还不符合垃圾回收的条件。
步骤7) 取消第 25 行和第 26 行的注释。保存、编译并运行代码。
步骤8) 此时,没有任何引用指向该对象,因此它符合垃圾回收的条件。垃圾回收器会将其从内存中移除,并且无法再将其检索出来。
如何删除对象 Java?
Java 它不提供手动删除操作符,因此标准做法是删除对该对象的所有引用,以便垃圾回收器可以回收它。
1) 要使对象符合垃圾回收的条件,请将指向该对象的所有引用变量赋值为 null。
2) 基本类型不是对象,因此不能赋值为 null。当周围的栈帧弹出时,它们的存储空间会自动回收。



















