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相关的书籍。
如果我的理解有什么不正确的地方,欢迎在评论区指出!