JVM内存区域<二>
一:深入理解运行时数据区
有如下代码:
JVM执行的具体流程如下:
1:JVM申请内存:JVM通过配置参数或者默认参数向操作系统申请内存,根据内存大小找到内存分配表,将内存段的起始和结束地址返回给JVM。
2:JVM通过配置初始化栈,堆和方法区的大小。
3:将class文件以及文件里面定义的常量和静态变量加载到方法区。
4:上面的代码new了对象,此时会在堆里面创建对象并属性赋值,栈里面会有一个地址指向堆上创建的对象。
二:对空间的划分
堆空间主要分为新生代和老年代。新生代又分为Eden(新创建了除了大对象外都放在该区域),Survivor 区。Survivor 区由Survivor From和Survivor To 区组成。当新生代发生垃圾回收时主要是采用复制算法垃圾回收(将根可达的对象复制到From区或者To区并年龄加一)。大对象或者对象对象年龄到达15,相同年龄的对象大小总和大于Survivor区大小的一半时,大于等于该年龄的对象也会放入老年代。
三:栈优化技术-----栈帧之间的数据共享
当出现有参数的方法调用时,JVM会进行一些优化,使上一个栈帧的操作数栈与下一个栈帧的局部变量表重合共享数据。这样的话不仅节约了空间而且在调用方法时可以共用数据不需要进行额外的复制处理。
四:常量池
1:class常量池(静态常量池):在class文件中除了有类的版本,方法,接口,字段外还有常量池(Constant Pool Table)主要是用来存放字面量以及符号引用。
字面量:
给基本类型变量赋值的方式就叫做字面量或者字面值。 比如:String a=“b” ,这里“b”就是字符串字面量,同样类推还有整数字面值、浮点类型字面量、字符字面量。
符号引用 :
符号引用以一组符号来描述所引用的目标。符号引用可以是任何形式的字面量,JAVA 在编译的时候一个每个 java 类都会被编译成一个 class 文件,但在编译的时候虚拟机并不知道所引用类的地址(实际地址),就用符号引用来代替,而在类的解析阶段(后续 JVM 类加载会具体讲到)就是为了把 这个符号引用转化成为真正的地址的阶段。 一个 java 类(假设为 People 类)被编译成一个 class 文件时,如果 People 类引用了 Tool 类,但是在编译时 People 类并不知道引用类的实际内存地址,因 此只能使用符号引用(org.simple.Tool)来代替。而在类装载器装载 People 类时,此时可以通过虚拟机获取 Tool 类的实际内存地址,因此便可以既将符号 org.simple.Tool 替换为 Tool 类的实际内存地址。
2: 运行时常量池是在类加载完成之后,将 Class 常量池中的符号引用值转存到运行时常量池中,类在解析之后,将符号引用替换成直接引用。
运行时常量池在 JDK1.7 版本之后,就移到堆内存中了,这里指的是物理空间,而逻辑上还是属于方法区(方法区是逻辑分区)。 在 JDK1.8 中,使用元空间代替永久代来实现方法区,但是方法区并没有改变。变动的只是方法 区中内容的物理存放位置,但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。
3:字符串常量池这个概念是最有争议的,我们从它的作用和 JVM 设计它用于解决什么问题的点来分析它。 以 JDK1.8 为例,字符串常量池是存放在堆中,并且与 java.lang.String 类有很大关系。设计这块内存区域的原因在于:String 对象作为 Java 语言中重要的数据类型,是内存中占据空间最大的一个对象。高效地使用字符串,可以提升系统的整体性能。
五:String 类分析(JDK1.8)
1:String对象的不可变性:String类是被final,变量char数组也被final修饰,所以String一旦被创建将不能修改。
2:String的创建以及内存分配方式
例如:String str = “abc”;首先会去字符串常量池中查找是否有abc有的话直接返回地址,没有的话则在字符串常量池创建abc并返回地址。
String str2 = new String(“abc”);此时还是查找字符串常量池是否有abc没有则创建,但是还会在堆中创建一个String指向常量池中的abc。
如果对象的属性是字符串,此时是会在堆中创建。例如person.setName(“xiaodai”);xiaodai则会在堆中创建。
3:String的intern()方法。
常年池中有相同的值则直接返回。
1:new String 会在堆中创建a的String对象,字符串常量池会创建king,此时调用intern()方法,发现字符串常量池中有king则直接返回常量池中king的地址。
2:new String 会在堆中创建b的String对象,,此时调用intern()方法,发现字符串常量池中有king则直接返回常量池中king的地址。
3:a和b都是指向常量池中的king,所以相等。