Java内存分析(1)——内存分区和分配过程
2016-12-29 23:52
246 查看
我们已经习惯于写出类似
如上图所示,JVM主要分为以上几块:程序计数器,本地方法栈,虚拟机栈,堆和方法区。稍微粗糙一些得分法是JVM分为栈和堆,栈包括虚拟机栈,本地方法栈,程序计数器,堆分为堆和方法区。需要说明的是这里所说的Java内存和硬件上的内存并不能完全对应,Java内存还可能包含CPU的寄存器,高速缓存等。
以上三块是伴随着线程同生共死的,生命周期由编译器控制。
其中,方法区中有一个运行常量池:存放的为类中的固定的常量信息、方法和Field的引用信息等。
一段司空见惯的代码:
内存存储详情图:
由图中所示:
栈存储的是局部变量,如上述代码中的stu1,stu2。
堆的heap区存储的是new创建出的变量。
堆的方法区存储的是类的方法(动态,静态),变量(动态,静态),常量。
我们知道:Java中,除了基本数据类型,都是引用类型,也就是说这些数据类型存的都是引用地址,直至一个接一个的引用,最终指向到基本类型,基本类型存储的才是值的本身。
这样就可以理解图中的所有箭头,一条箭头代表一个引用,箭尾是引用的地址,箭尖是被引用的变量。
由此,我们来剖析代码在内存中是如何实现的:
1,首先JRE加载了编译好的class文件,将相关类的方法,变量,常量都放到了类区。然后开始执行语句。
2,执行第一句语句
按照Java由右向左逐步执行的机制,首先是运行new Student()在Java Heap中开辟出一个可以存储Student类所有属性和方法的引用的连续空间。方法都指向了方法区的对应方法,而属性在未赋值的情况下都是null或0。
之后是执行Student stu1在栈空间内分配一个空间存储一个存储Student类型变量引用的空间。
最后运行等号,等号就是将变量区中变量的地址赋值给栈中的变量,即建立了引用关系。
3, 执行第二句:
以此类推,以下都如图所示。被赋值的变量存储了相应的地址,未赋值的保持为空。
4,值得注意的是,Java Heap的变量也可以相互引用,例如
静态变量与方法与与一般变量和方法所不同的是,在用new关键词创建类对象时,这个类里面的static变量和方法不会被初始化,栈中的对象直接对它们进行引用。因此,静态方法无需实例化即可使用,静态变量被类的所有实例所共享。
顺带提一句垃圾回收机制(garbage collection),Java采用的是自动的垃圾回收机制,当虚拟机,发现某一个变量在之后不会被使用到,例如图中stu1已经用完了,就会把stu1引用的一整块在变量区的内容释放掉。
垃圾回收是一个后台线程,在不知不觉中帮我们管理着内存,好处是解放了程序员,编写程序少操了一份心,坏处是相比C++这种需要主动释放内存的语言少了一点灵活性。Java可以使用System.gc()来提醒虚拟机可以考虑进行一下垃圾回收,但是系统可能不一定听你的,你可以建议,但做不做由它,Java这种脾气在很多地方都有体现。
总结一下,
Java内存管理分为两大块,栈和堆,堆又分为变量区和类区;
栈引用变量区,变量区引用类区,变量区还相互引用,类区内部也相互引用;
Java的有自动的垃圾回收机制,可以将不再被使用的变量释放。
核心:一定要深入理解Java的引用机制。
Object obj=new Object();型的语句,然而背后究竟发生了什么?从JVM内存结构分析更有助于加深理解记忆。下面试着举例说明:
JVM内存分区:
如上图所示,JVM主要分为以上几块:程序计数器,本地方法栈,虚拟机栈,堆和方法区。稍微粗糙一些得分法是JVM分为栈和堆,栈包括虚拟机栈,本地方法栈,程序计数器,堆分为堆和方法区。需要说明的是这里所说的Java内存和硬件上的内存并不能完全对应,Java内存还可能包含CPU的寄存器,高速缓存等。
程序计数器
程序计数器是用于存储每个线程下一步将执行的JVM指令,如该方法为native的,则程序计数器中不存储任何信息。本地方法栈
众所周知,Java的底层是C实现的,所有调用的C代码都被称为本地方法(native method)。JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。虚拟机栈
JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址。以上三块是伴随着线程同生共死的,生命周期由编译器控制。
堆 Java Heap
它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Java Heap是线程共享的。Heap中的对象的内存需要等待GC进行回收。方法区
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息。方法区域也是全局共享的,在一定的条件下它也会被GC。其中,方法区中有一个运行常量池:存放的为类中的固定的常量信息、方法和Field的引用信息等。
内存分配过程
了解了JVM的内存分区,接下来就以实例分析到底变量都存放在哪里。由于下面的例子和本地方法栈,程序计数器关系不大,所以暂时略去。一段司空见惯的代码:
public class student{ String name; Integer age; String score public void study(){ system.out.println("I am studing"); } public void sayHello(){ system.out.println("Hello"); } } public class computer(){ String brand; String speed; } public class test(){ public static void main(String args[]){ Student stu1 =new Student(); stu1.name="wang"; stu1.age=24; Student stu2=new Student(); stu2.name="li"; stu2.age=23; stu1.computer.brand="Lenovo"; stu1.speed="2.5GHz" } }
内存存储详情图:
由图中所示:
栈存储的是局部变量,如上述代码中的stu1,stu2。
堆的heap区存储的是new创建出的变量。
堆的方法区存储的是类的方法(动态,静态),变量(动态,静态),常量。
我们知道:Java中,除了基本数据类型,都是引用类型,也就是说这些数据类型存的都是引用地址,直至一个接一个的引用,最终指向到基本类型,基本类型存储的才是值的本身。
这样就可以理解图中的所有箭头,一条箭头代表一个引用,箭尾是引用的地址,箭尖是被引用的变量。
由此,我们来剖析代码在内存中是如何实现的:
1,首先JRE加载了编译好的class文件,将相关类的方法,变量,常量都放到了类区。然后开始执行语句。
2,执行第一句语句
Student stu1=new Student();
按照Java由右向左逐步执行的机制,首先是运行new Student()在Java Heap中开辟出一个可以存储Student类所有属性和方法的引用的连续空间。方法都指向了方法区的对应方法,而属性在未赋值的情况下都是null或0。
之后是执行Student stu1在栈空间内分配一个空间存储一个存储Student类型变量引用的空间。
最后运行等号,等号就是将变量区中变量的地址赋值给栈中的变量,即建立了引用关系。
3, 执行第二句:
stu1.name="wang";“wang”是一个已经存在于方法区的常量,stu1.name是变量区中的一个空变量,等号将两者连接,将“wang”的地址赋给了stu1.name。
以此类推,以下都如图所示。被赋值的变量存储了相应的地址,未赋值的保持为空。
4,值得注意的是,Java Heap的变量也可以相互引用,例如
stu1.computer就是从一个变量引用到另一个变量。
静态变量与方法与与一般变量和方法所不同的是,在用new关键词创建类对象时,这个类里面的static变量和方法不会被初始化,栈中的对象直接对它们进行引用。因此,静态方法无需实例化即可使用,静态变量被类的所有实例所共享。
顺带提一句垃圾回收机制(garbage collection),Java采用的是自动的垃圾回收机制,当虚拟机,发现某一个变量在之后不会被使用到,例如图中stu1已经用完了,就会把stu1引用的一整块在变量区的内容释放掉。
垃圾回收是一个后台线程,在不知不觉中帮我们管理着内存,好处是解放了程序员,编写程序少操了一份心,坏处是相比C++这种需要主动释放内存的语言少了一点灵活性。Java可以使用System.gc()来提醒虚拟机可以考虑进行一下垃圾回收,但是系统可能不一定听你的,你可以建议,但做不做由它,Java这种脾气在很多地方都有体现。
总结一下,
Java内存管理分为两大块,栈和堆,堆又分为变量区和类区;
栈引用变量区,变量区引用类区,变量区还相互引用,类区内部也相互引用;
Java的有自动的垃圾回收机制,可以将不再被使用的变量释放。
核心:一定要深入理解Java的引用机制。
相关文章推荐
- Java程序运行内存分配分析
- 深入理解java虚拟机(六):java垃圾收集分析实战(内存分配与回收策略)
- java中类加载的全过程及内存图分析
- java 调用过程的内存分配情况
- Java对象创建过程和内存结构分析
- Java中的成员初始化顺序和内存分配过程
- ART运行时为新创建对象分配内存的过程分析
- ART运行时Compacting GC为新创建对象分配内存的过程分析
- java内存分配分析/栈内存、堆内存
- 详细记录一下JAVA应用程序服务出现内存溢出的利用MAT分析过程
- 分析Java内存分配的栈、堆以以及常量池详细
- java内存分配分析/栈内存、堆内存
- java内存分配分析/栈内存、堆内存
- 函数调用和栈的内存分配过程分析
- OO 中的继承分析:主要分析在编译和运行过程中 子类、父类 的字段和方法以及实例化时候在内存中分配 和 执行的先后,以及两个原则
- 从内存分配角度分析c和java里的static 关键字.
- java内存分配分析/栈内存、堆内存
- [转]使用Java Mission Control进行内存分配分析
- java内存分配分析
- 内存分析----------------图解Java继承内存分配