Java虚拟机JVM复习之jvm内存模型

1. 程序计数器,线程私有,用来标记当前线程所执行的字节码的行号。
为什么要有程序计数器昵?
在任一确定时刻,一个处理器(多核处理器中一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。
2.虚拟机栈
线程私有,即“栈内存”,JVM栈帧包括如下几部分:
局部变量表:用来存放方法参数和方法内部定义的局部变量,JVM通过索引来调用局部变量表。局部变量表的第0位是一个指向当前对象的引用,也就是Java里的this。
操作数栈:用于存放JVM从局部变量表复制的常量、变量以及计算结果的中间量。
动态连接:保存的是一个指向运行时常量池中该栈帧所属方法的引用(指向常量池对应栈帧的引用)。
返回地址,一般指向方法的返回结果。
3. 本地方法栈
本地方法栈和虚拟机栈的作用类似,只是虚拟机栈中执行的是java方法服务,而本地方法栈则为虚拟机使用Native方法服务。
4. java堆
jvm管理内存中最大的一块,线程共享,存放的是所有对象实例及数组。
5. 方法区
线程共享,用于存放已经被JVM加载的类信息、常量、静态变量、即时编译器编译的代码等数据,其大小不固定、也不一定连续。
常量池,java中有三种常量池,字符串常量池、class常量池、运行时常量池。
1)字符串常量池
在jdk1.6以及之前的版本,字符串常量池是放到方法区中的,在1.7及之后就被移到了中;在HotSpot VM里实现string pool功能的是一个StringTable类,它是一个Hash表,默认长度是1009;StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。字符串常量由一个个字符组成,放在了StringTable上。
字符串常量池里放的是什么?
在JDK6.0及之前的版本中,String Pool里放的都是字符串常量;在JDK7.0中,String Pool中可以存放放于堆内的字符串对象的引用。
2)class常量池
每一个Java类都会被编译成class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译器生成的各种字面量和符号引用,每个class文件都有一个class常量池。
什么是字面量和符号引用:
  • 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
  • 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。
3)运行时常量池
运行时常量池存在于方法区中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加,符号引用可以被解析为直接引用。当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。
6.直接内存
又称堆外内存,也就是说这不是jvm运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但这部分也会被频繁的使用,而且也可能导致OOM(超过物理内存)。
基本数据类型及引用数据类型的存放位置详解。
(1)在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就是局部变量只能在方法中有效的原因。 在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。a.当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在方法栈中。b.当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的。
(2)在类中声明的变量是成员变量,也叫全局变量,放在堆中的(全局变量不会随着某个方法执行结束而销毁,但是生命周期和对象的实例相同)。在类中声明的变量即可是基本类型的变量也可是引用类型的变量。a.当声明的是基本类型的变量,其变量名及其值放在堆内存中的。b.当为引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中。
JMM中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量

volatile : ①保证此变量对所有线程的可见性,即当一条线程修改了这个值,新值对于其他所有线程来说是立即得知的,普通变量需要通过主内存传递。②禁止指令重排序优化。
(1)当一个线程要使用共享内存中的 volatile 变量时,它会直接从主内存中读取,而不使用自己本地内存中的副本。(2)当一个线程对一个 volatile 变量进行写时,它会将这个共享变量的值刷新到共享内存中。
其实 volatile 变量保证的是一个线程对它的写会立即刷新到主内存中,并置其它线程的副本为无效,它并不保证对 volatile 变量的操作都是具有原子性的。
synchronized(悲观锁机制):使得它作用范围内的代码对于不同线程是互斥的,并且线程在释放锁的时候会将共享变量的值刷新到主内存中。
1、当线程对 volatile变量写时,java 会把值刷新到共享内存中;而对于synchronized,指的是当线程释放锁的时候,会将共享变量的值刷新到主内存中。
2、线程读取volatile变量时,会将本地内存中的共享变量置为无效;对于synchronized来说,当线程获取锁时,会将当前线程本地内存中的共享变量置为无效。
3、synchronized 扩大了可见影响的范围,扩大到了synchronized作用的代码块。
final关键字可以修饰变量、方法和类。 final 变量一经初始化,就不能改变其值。
这里的值对于一个对象或者数组来说指的是这个对象或者数组的引用地址。因此,一个线程定义了一个final变量之后,其他任意线程都可以拿到这个变量。但有一点需要注意的是,当这个final变量为对象或者数组时,1、虽然我们不能将这个变量赋值为其他对象或者数组的引用地址,但是我们可以改变对象的域或者数组中的元素。 2、线程对这个对象变量的域或者数据的元素的改变不具有线程可见性。
Java中哪些组件需要使用内存
(1)Java堆:存储Java对象
(2)线程:Java运行程序的实体
(3)类和类加载器:存储在堆中,这部分区域叫永久代(PermGen区)
(4)NIO:基于通道和缓冲区来执行I/O的新方式。
JVM内存分配策略
1)对象优先分配在Eden
2)大对象直接进入老年代
3)长期存活的对象将进入老年代
4)幸存区相同年龄对象的占幸存区空间的多于其一半,将进入老年代
5)空间担保分配(老年代剩余空间需多于幸存区的一半,否则要Full GC
本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
极客文库 » Java虚拟机JVM复习之jvm内存模型

Leave a Reply

欢迎加入「极客文库」,成为原创作者从这里开始!

立即加入 了解更多