java虚拟机概览

来源:互联网 发布:sql语句中not in 编辑:程序博客网 时间:2024/06/07 21:00

一、java虚拟机概览



说明:
1、程序计数器:是内存一块小的区域,是当前线程所执行字节码的行指示器;每个线程都有独立的程序计数器,互不影响是,是线程私有的;
2、java虚拟机栈:它的生命周期与线程相同,是描述java方法执行的内存模型,java虚拟机栈也叫作局部变量表;每个方法执行时,都会创建一个栈帧,用于存储局部变量表
操作栈,动态链接、方法出口等信息,每个方法从开始到执行完毕,就相当于入栈、出栈的过程;
局部变量表:各种基本数据类型(boolean,byte、char、short、int、float、long、double),对象引用(reference类型),局部变量表所需的空间是在编译器完成的,在运行期不会改变局部变量表的大小;
异常类型:StackOverflowError和OutOfMemory,StackOverflowError:当线程请求栈的深度大于虚拟机所允许的深度时,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存时,就会抛出OutOfMemoryError
3、本地方法栈:本地方法栈和java虚拟机栈类型,区别则是为虚拟机使用到的native方法服务的;
异常类型:本地方法栈抛出的异常类型和java虚拟机栈相同:StackOverflowError和OutOfMemory,StackOverflowError。
4、方法区(method area):方法区和堆一样,是线程共享的,用于存放已被虚拟机加载的类信息,常量,静态变量,即编译后的代码等数据,虽然虚拟机把方法区作为java堆的一部分,但是它确有另外一个别名,即非堆(NON-HEAP),目的是和堆区分开来。
异常类型:当方法区无法满足内存分配时,会抛出OutOfMemoryError异常;
5、java堆:是java虚拟机管理内存的最大一部分,是线程共享的一个区域;在虚拟机启动时,它的唯一目的就是存放实例对象及相关数组;java堆是内存回收的主要区域,java堆可以是物理上不连续的堆存空间,但在逻辑上必须是连续的,这点和我们磁盘有点类似;这块内存区域是可以扩展的,也可以设置成固定大小,是通过参数(-Xmx和-Xms控制)
异常类型:OutOfMemoryError,当对象或数组在堆中没有完成内存分配时,并且堆也是无法扩展时,此时会报OutOfMemory异常;
堆空间细分:堆可以新生代和老年代,再细分可以分为Eden区、From Survivor区,To  Survivor ,这样做的目的其实是便于内存管理和内存回收。
总结:线程私有的内存区域有:程序计数器,

二、java虚拟机的对象分配

这里说的对象是指普通的java对象,不包括数组和Class对象,按照对象的生命周期来说,无非经历这么几步:创建、使用,回收;先看看对象的创建:
1、对象的创建:当虚拟机遇到new时,首先会检这个指令的参数是否能在常量池中定位到另一个类的符号引用,这个类的符号引用就是类的加载、解析和初始化工作;古国不存在,那么必须执行类的加载;
2、类的加载通过后,那么首先接下来虚拟机就为新对象分配内存,对象所需要的内存大小在加载类完成后就完全确定了,java内存的分配一般使用指针碰撞和空闲列表方法,具体采用哪一种方法需要根据java内存是否规整和使用的垃圾收集器是否带有压缩功能决定的,假设内存是规整的,指针碰撞就是虚拟机把使用过的内存放在一边,没有使用过的内存放在另一边,中间使用指针作为分界点的指示器,对象的分配内存就是把这个指针向空闲内存那边移动一段与对象大小相等的距离,这种方式叫指针碰撞(bump the pointer);如果java内存不是规整的,就无法使用指针碰撞方法了,此时虚拟机内维护一个列表,这个列表记录了内存的使用情况,在分配对象的时候,就从列表中找到一块足够大的空间划分给对象实例,然后在更新这个内存列表;这种分配方式叫做空闲列表方法(Free  list)。虚拟机使用serial 和parnew等带有压缩功能时候,分配内存的方式是指针碰撞,使用cms这种基于mark-sweep收集器使用的空闲列表方法;其次,java的内存分配是非常频繁的,即使是移动指针也不够的,还有并发的情况呢,解决这种问题有两种办法:一种是cas(compare and swap)和失败重试的方式保证更新操作的原子性,另外一种就是按照线程划分在不同的空间中分配,也就是说每个线程在java堆中预分配一块内存,这块内存叫(Thread Local Allocation Buffer)本地线程分配缓冲区,简称TLAB,例如:线程A需要分配内存,那么就在线程A的TLAB上分配,当A的TLAB用完后,需要同步锁定。虚拟机是否需要TLAB,需要通过参数来设定,-XX:UseTLAB,;当所有的内存分配完成之后,虚拟机需要将分配的内存初始化为0,这一步也就是java变量在不赋值时可以直接使用的原因,例如:int a;那么a的值为0;此时从虚拟机的角度来说,一个新的对象就产生了,但是从java程序的角度来说,还差点事吧,这种对象也是无法程序员的意愿来工作的,因此必须初始化才算完整,一般对象创建new后,会执行init方法进行初始工作;
3、对象的内存布局:对象在存储布局中分为3块:对象头(Header)、实例数据(Instance data)、对其填充(Padding),对象头包含的信息:哈希码、gc分代年龄、锁状态标志、时间戳和类型指针,类型指针说白了就是对象指向它的类元数据指针,虚拟机就是通过这个来确定这个对象是哪个类的实例;实例数据(Instance data)才是对象真正存储的对程序员来说的有效信息,就是咱们在程序中定义的变量内容,这个变量内容无论是从父类继承过来,还是自身定义的,都需要记录下来,记录顺序受到虚拟机的分配策略和字段在源码中的位置有关,相同宽度的字段总是被分配到一起,在满足这个条件下,在父类中定义的变量会出现子类之前;对其填充主要是起到占位符的作用;


原创粉丝点击