Java中的内存分配

231 阅读5分钟

java程序运行时内存分配详解

刚刚学完java的内存分配,感觉很多东西咋就没见过呢....不行不行,必须写篇文章记录一下,不然...不然过几天就又忘光了!!

内存顾名思义就是内存条,任何应用程序如果想要运行,都必须开辟一块内存空间。

但是整体的内存是由操作系统统一管理的,java程序想要运行就得申请一块,注意注意,申请过来的内存不是一整块,要把它划分成5个区域。

可能这里有些人就说了,整个一整块内存不挺好的吗?为什么还要划分呢?

这里要注意,这个道理跟房子是一样的,每个区域有每个区域的作用,卧室用来睡觉、厨房用来吃饭.....

总不能说在厨房睡觉,在卧室.....

好啦,开始进入正题,我们就先从基本概念开始讲讲。

一、 基本概念

每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈。

进程所创建的所有类的实例(也就是对象)或数组(指的是数组的本身,不是引用)都放在堆中,并由该进程所有的线程共享。

Java中分配堆内存是自动初始化的,即为一个对象分配内存的时候,会初始化这个对象中变量。

虽然Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也就是说在建立一个对象时在堆和栈中都分配内存,在堆中分配的内存实际存放这个被创建的对象的本身,而在栈中分配的内存只是存放指向这个堆对象的引用而已

局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。

二、具体的概念

JVM的内存可分为5个区:

堆区(heap): 储存 new 出来的东西

  • 堆内存里面的东西都有一个地址值:16进制的
  • 堆内存里面的数据,都有默认值。规则

如果是整数:默认为0

如果是浮点数:默认为0.0

如果是字符:默认为‘\u0000’

如果是布尔:默认为false

如果是引用类型:默认为null

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令) ; 2.jvm只有一个堆区(heap),且被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身和数组本身;

栈区(stack): 储存方法中的局部变量,方法运行一定要在栈当中

  • 局部变量:方法的参数,或者是方法{}内部的变量。
  • 作用域:一旦超出作用域,立刻从栈内存当中消失。

1.每个线程包含一个栈区,栈中只保存基础数据类型本身和自定义对象的引用; 2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问; 3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令);

方法区(静态区)(method area): 储存.class相关信息,包含方法的信息

1.被所有的线程共享,方法区包含所有的class(class是指类的原始代码,要创建一个类的对象,首先要把该类的代码加载到方法区中,并且初始化)和static变量。 2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。 **本地方法栈:**与操作系统相关。

**寄存器:**与CPU相关。

三、具体操作

前面我们已经了解了java里面的内存划分

当我们写上一段代码,来玩一个数组的时候,内存当中到底过程是怎样的?

一个数组从无到有,从有到变分别是怎样的过程?

就比如我们现在来看一段代码

/**
 * @author iwen大大怪
 */
public class HelloJava {
    public static void main(String[] args){
        // 动态初始化
        int[] array = new int[3];
        System.out.println(array);
        System.out.println(array[0]);  
        System.out.println(array[1]);
        System.out.println(array[2]);
        System.out.println("=============");

        // 改变数组中的元素
        array[1] = 10;
        array[2] = 20;
        System.out.println(array);
        System.out.println(array[0]);
        System.out.println(array[1]);
        System.out.println(array[2]);
    }
}

我们再来分析一下这一块代码:

前面我们说过,JVM大致使用的就是内存的3个部分,栈、堆、方法区

方法区存放的是.class相关信息,所以这里就把**public static void main(String[] args)**直接放入其中,方法区中保存的就是方法的信息,比如你叫什么名字呀、你的返回值是什么呀等等

栈中就存放方法的参数、局部变量,所以这里就把**main(String[] args)**放入其中,并且在栈中开辟一块内存。这个动作叫做进入栈内存

在**main(String[] args)**里面要做什么呢?

首先我们来看一下main代码下面第一行,就是要创建一个int[] array数组,数组名称就是一个局部变量

紧接着就是new int[3];这里代码new了一个数组,前面说过,凡是new出来的都存放在堆当中,所以就在堆里开辟一块内存空间,因为我们定义时将这个数组定义长度为3,所以将内存分为3份,并且给他们标上下标,同时,整个new出来的数组在堆中也是有一个地址,整个地址存放在栈中,如上图

当我们只用array访问时,表示的是栈里保存的那个地址,映射的是堆中new出来的数组的地址。

当我们使用array[0]访问时,表示的是直接访问堆中new出来数组的0号下标的元素。

总结

java内存分配条理还是很清楚的,如果要彻底搞懂,可以去查阅JVM相关的书籍。

如果我的理解有什么不正确的地方,欢迎在评论区指出!