Java 中的内存管理

什么是栈内存?

Java中的栈(Stack)是内存中用于包含方法、局部变量和引用变量的部分。栈内存始终以“后进先出”(LIFO)的顺序进行引用。局部变量在栈中创建。

什么是堆内存?

堆(Heap)是内存中用于包含对象的部分,也可能包含引用变量。实例变量在堆中创建。

Java中的内存分配

Java中的内存分配是指将程序的虚拟内存部分划分为特定区域,用于存储变量以及结构和类的实例。然而,内存并非在声明时分配给对象,而仅仅是创建一个引用。对象的内存分配使用`new()`方法,因此对象总是分配在堆内存上。

Java内存分配分为以下几个部分:

  1. Stack
  2. 代码
  3. 静态

这种内存划分是为了其有效的管理。

  • 代码部分包含您的字节码
  • 内存的部分包含方法、局部变量和引用变量
  • 部分包含对象(也可能包含引用变量)。
  • 静态部分包含静态数据/方法

局部变量和实例变量的区别

实例变量声明在类内部,但不在方法内部

class Student{ 
int num; // num is  instance variable 
public void showData{}

局部变量声明在方法内部,包括方法参数

public void sum(int a){

int x = int a +  3;

// a , x are local variables;

}

栈和堆的区别

如果视频无法访问,请点击此处

我们用一个例子来更好地理解这一点。假设我们的main方法调用了m1方法

public void m1{
int x=20
}

在Java的栈中,会为m1方法创建一个栈帧。

Java Stack and Heap

m1中的变量X也会在栈中m1的栈帧里创建。(见下图)。

Java Stack and Heap

方法m1调用方法m2。在Java的栈中,会在m1栈帧的顶部创建一个新的m2栈帧。

Java Stack and Heap

Java Stack and Heap

变量b和c也会在栈中m2的栈帧里创建。

public void m2(int b){
boolean c;
}

Java Stack and Heap

同样,方法m2调用方法m3。又会在栈顶创建一个m3栈帧(见下图)。

Java Stack and Heap

Java Stack and Heap

现在假设我们的m3方法正在创建一个“Account”类的对象,该类有两个实例变量 int p和int q。

Account {
             Int p;
             Int q;
       }

这是m3方法的代码:

public void m3(){
	Account ref = new Account();
	// more code
}

语句 `new Account()` 会在堆中创建一个Account对象。

Java Stack and Heap

引用变量“ref”会在Java的栈中创建。

Java Stack and Heap

赋值“=”运算符将使引用变量指向堆中的对象。

Java Stack and Heap

一旦方法完成了它的执行。控制流将返回到调用方法。在本例中是m2方法。

Java Stack and Heap

来自m3方法的栈会被清空。

Java Stack and Heap

由于引用变量将不再指向堆中的对象,它将被垃圾回收。

Java Stack and Heap

一旦m2方法完成了它的执行。它将被从栈中弹出,它的所有变量都将被清除,并且不再可用。

对m1方法也同样如此。

最终,控制流将返回到程序的起始点。通常是“main”方法。

如果对象将其实例变量作为引用怎么办?

public static void main(String args[]) {
  A parent = new A(); //more code } class A{ B child = new B(); int e; //more code } class B{ int c; int d;  //more code }

在这种情况下,引用变量“child”将在堆中创建,它又会指向它的对象,类似于下图所示。

Java Stack and Heap

Java中的垃圾回收是什么?

Java垃圾回收是一个程序自动执行内存管理的过程。垃圾收集器(GC)会查找未使用的对象并删除它们以回收内存。在Java中,对象的动态内存分配是通过`new`运算符实现的,它会占用一些内存,并且直到有引用指向该对象时,内存才会一直分配。

当没有对象引用时,它就被认为不再需要了,并且可以回收对象占用的内存。由于Java会自动处理内存释放,因此没有必要显式销毁对象。

实现这一目标的技术被称为垃圾回收。不释放内存的程序最终可能会因系统没有剩余内存可分配而崩溃。这些程序被称为存在内存泄漏Java中的垃圾回收在程序生命周期中自动发生,无需手动释放内存,从而避免内存泄漏。

在C语言中,程序员有责任使用`free()`函数释放动态分配的内存。这正是Java内存管理优于C语言的地方。

注意:所有对象都在内存的堆部分创建。更多内容将在之后的教程中介绍。

示例:学习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)保存、编译并运行代码。如图所示,创建了两个对象和两个引用变量。


 Garbage Collector Mechanism

步骤3)取消注释第20、21、22行。保存、编译并运行代码。

步骤4)如下图所示,两个引用变量指向同一个对象。

Garbage Collector Mechanism

步骤5)取消注释第23行和第24行。编译、保存并运行代码。

步骤6)如下图所示,s2变为null,但s3仍然指向该对象,并且不符合Java垃圾回收的条件。

 Garbage Collector Mechanism

步骤7)取消注释第25行和第26行。保存、编译并运行代码。

步骤8)此时,没有任何引用指向该对象,它已符合垃圾回收条件。它将被从内存中移除,并且无法找回。

 Learn Garbage Collector

如何在Java中删除一个对象?

1)如果你想让你的对象符合垃圾回收条件,请将其引用变量赋值为null。

2)基本类型不是对象。它们不能赋值为null。

How to delete an object in Java

摘要

  • 当调用一个方法时,会在栈顶创建一个栈帧。
  • 一旦方法执行完毕,控制流将返回到调用方法,其相应的栈帧将被清除。
  • 局部变量在栈中创建。
  • 实例变量在堆中创建,是其所属对象的一部分。
  • 引用变量在栈中创建。