JVM进阶系列(一)

JVM进阶系列(一)

1、引言

Java语言具有一次编写、到处运行的特性。主要原因就是JVM,JVM严格来说就是一种虚拟机的规范。Java是一种解释和编译都存在的语言,它一开始是由javac命令将.java的源文件编译成.class文件(字节码文件),之后在由JVM虚拟机解释运行在系统上。这就是为什么Java语言能够一次编写、到处运行。

2、内部结构

  • ​ 程序计数器:这部分是一块较小的内存空间,被看做为当前线程所执行的字节码的行号指示器。字节码解释器就是通过改变这个计数器的值选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复都依靠他。属于线程隔离
  • Java虚拟机栈:生命周期与线程相同,每个方法执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息属于线程隔离
  • 本地方法栈:与虚拟机栈发挥的作用是非常相似的,区别就是虚拟机栈为虚拟机执行Java方法(字节码)服务,本地方法栈则为虚拟机使用本地方法服务。属于线程隔离
  • Java堆:虚拟机管理的最大的一块内存。主要是存放所有对象的实例和数组属于线程共享的
  • 方法区:主要是用于存储被虚拟机加载的类型信息、常量、静态变量、运行常量池、构造函数信息、字段、方法等数据,即编译后的代码缓存等数据属于线程共享的

3、HotSpot虚拟机对象

3.1 对象的内存布局

对象属于类的实例,存储在Java堆中,对象在堆的存储布局可以分为三个部分:对象头(header)实例数据(Instance Data)对齐填充(Padding)。对象头主要是存储两种类型的数据,第一类是存储对象自身运行数据,如哈希码、GC 分代年龄、锁状态标志、偏向线程ID、偏向时间戳等。第二种数据,主要指的是类型指针,主要是对象指向它的类型元数据指针。实例数据部分存储的则是对象真正有效信息,即在代码里面定义的各种类型的字段内容。对象的第三部分对齐填充,没有特殊含义,并不是必然存在的。

3.2 对象的访问定位

​ Java程序主要是依靠Java栈上的reference数据操作对象上的具体数据。这个reference数据只是对象的引用,但是并没有定义这个引用通过什么方式去定位、访问到堆中的具体位置。所以对象的访问方式主要是依靠虚拟机实现的,分为两种:句柄访问,即在堆中划分一块内存作为句柄池。此时reference中存储的是对象的句柄地址,句柄中则包含的对象实例数据与类型数据的地址信息。直接指针访问,reference中存储的则是对象的地址,可以减少句柄访问的开销

3.3 OutOfMemeoryError 异常

  • Java堆溢出:java.lang.OufOfMemoryError:Java heap space 主要是因为内存中某个对象导致了OOM。有两种类别分别是:内存泄露、内存溢出。内存泄露是因为无用对象因为引用的存在,对象迟迟不能回收,内存占用越来越高,最终导致溢出。内存溢出,则是简答的程序想要获取内存的时候没有新的内存可以使用了。
  • 虚拟机栈和本地方法栈溢出:如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。虚拟机允许动态扩展,当栈容量无法申请到足够内存时,将抛出OufOfMemoryError异常。注意:HotSpot虚拟机只会抛出StackOverflowError异常。
  • 方法区和运行时常量池溢出:java.lang.OutOfMemoryError: PermGen space
  • 直接内存溢出:直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范的定义的内存区域。直接内存的频繁使用也会导致溢出。

你可能感兴趣的:(jvm,java)