您的位置:首页 > 编程语言 > Java开发

Java内存区域及内存分配

2018-03-30 10:14 176 查看
1、Java内存区域

(1)线程私有

虚拟机栈:主要是来描述java方法的内存模型。每个方法在执行时都会创建一个栈帧,用户存储局部变量表,操作数栈,动态链接,方法出口的信息。每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

本地方法栈:为虚拟机使用到的Native方法服务

程序计数器:当前线程所执行的字节码的行号指示器

(2)线程公有

堆:JVM所管理的内存中最大的一块。唯一目的就是存放实例对象。几乎所有对象实例都在这里分配。java堆是垃圾收集器管理的主要区域

方法区:用户存储已被虚拟机加载的类信息常量静态常量,即时编译器编译后的代码等数据

常量池:用户存放编译器生成的各种字面量和符号引用。

2、内存分配

java程序需要通过栈上的reference数据来操作堆上的对象

对象的访问定位:句柄访问、直接指针访问

(1)句柄访问

Java堆中会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对实例数据与类型数据的各自具体的地址信息



(2)直接指针访问

reference中存储的直接就是对象地址



方法区中的常量池

1、静态常量池:即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串字面量,还包含类、方法的信息,占用class文件绝大部分空间。

2、运行时常量池,则是jvm虚拟机在完成类装载后,将class问文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。

例子如下:

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;

System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // true
System.out.println(s1 == s4);  // false
System.out.println(s1 == s9);  // false
System.out.println(s4 == s5);  // false
System.out.println(s1 == s6);  // true


s1 == s2 : s1、s2在赋值时,均使用的字符串字面量。在编译期间,这种字面量会直接放入class文件中的常量池中,从而实现复用,载入运行时常量池后,s1、s2指向的是同一内存地址,所以相等。

s1 == s3:s3虽然是动态拼接出来的字符串,但是所有参与拼接的部分都是已知的字面量,在编译期间,这种拼接会被优化,编译器直接帮拼好,因此s3 = “Hel” + “lo” 最终被优化成s3 = “Hello”,所以相等。

s9 是由s7与s8两个变量拼接好的。s7与s8在方法区中,拼接后s9被分配到了堆内存中。所以s1与s9不等

s1 == s6是因为intern方法,s5在堆中,intern会将hello字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了hello字符串,所以intern方法直接返回地址。所以s1与s6相等

必须要关注编译期的行为,才能更好的理解常量池

运行时常量池中的常量,基本来源与class文件中的常量

程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则JVM不会自动添加常量到常量池。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: