《深入理解Java虚拟机》第1,2章

前言

之前对JVM的了解纯限于面经,或是牛客选择题后的解析。现在开始正式修炼,修炼!!


第一部分:走进Java

原来Java虚拟机上可以运行很多其他语言。Java平台的多语言混合编程成为主流。
JDK1.5加入了java.util.concurrent包
JDK1.7加入了forkjoin包,Fork/Join模式能解决不少并发问题。

64位虚拟机的运行比32位的落后约15%。但企业对64位虚拟机的需求是非常迫切的。JDK1.6 Update14后,提供了普通对象指针压缩功能,在解释器解释字节码时,植入压缩指令以节省内存消耗。

Open JDK是Sun JDK的开源版本。
我们使用Open JDK一探JDK内部的实现机制。

cygWin的安装只说一点是:默认安装缺少部分我们想要的包,要通过搜索包,然后先按分类找,可能会有包不在所给分类的状况,我们就去其他分类对照描述来选择包,点击切换skip的箭头,既是选择。

Too young too simple


第二部分:自动内存管理机制

正是因为Java程序员把一切内存控制权力交给虚拟机,一旦出现内存泄漏和溢出,如果不了解虚拟机是怎样使用内存的,排查错误会成为一项艰难的工作。

  • 方法区Method Area——VM Stack虚拟机栈 Native Method Stack本地方法栈
  • 堆Heap——————— Counter程序计数器
  • 执行引擎 ————–》 本地库接口——》本地方法库

2.2.1 程序计数器
为了线程切换后能恢复到正确的执行位置存在
字节码解释器通过改变这个计数器的值选取下一条需要执行的字节码指令
每个线程都有一个独立的程序计数器,各独立存储,这类内存区域叫线程私有的内存。
如果线程正在执行Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;
如果线程在执行Native方法,这个计数器值则为空,此内存区域唯一没有规定OutOfMemoryError的区域。


2.2.2 Java虚拟机栈
线程私有,生命周期和线程相同。虚拟机栈描述的是Java方法执行的内存模型。
每个方法执行都会创建栈帧,存储局部变量表,操作栈,方法出口等。局部变量表所需空间在编译期分配
每个方法被调用直至完成的过程,对应着一个栈帧在VM stack从入栈到出栈的过程。
线程请求的栈深度大于VM允许的深度,将抛出StackOverFlow异常,VM扩展时无法申请到足够内存,抛出OutOfMemory异常。


2.2.3 本地方法栈
Native method stack为虚拟机使用的Native方法服务。本地方法栈中的方法,具体的虚拟机可自由实现。


2.2.4 Java堆
Java堆是被所有线程共享的内存区域,在虚拟机启动时创建。所有的对象实例和数组都要在堆上分配。也是垃圾收集器管理的主要区域。Java堆可以处于物理不连续的内存空间中。
2.2.5 方法区
方法区和堆,都是线程共享的区域。存储已被虚拟机加载的类信息,常量,静态变量,编译后的代码。为堆的逻辑部分。和堆一样不需要连续的内存和可以选择固定大小或者可扩展外,可以不实现垃圾收集。这个区域回收主要是针对常量池的回收和对类型的卸载。


2.2.7 直接内存
直接内存用不好也导致OutOfMemory的出现。JDK1.4加入了NIO类。这是基于Channel和Buffer的I/O方式。可以使用Native函数类直接分配堆外内存。通过堆中的DirectByteBuffer对象作为这块内存的引用进行操作。避免了Java堆和Native堆中来回复制数据。


2.3对象访问
O o=new O();
前者作为引用类型出现,反映到Java栈的本地变量表中,后者反映到Java堆中。Java堆中还必须包含能察州道此对象类型数据(对象类型、父类、实现的接口、方法等)的地址信息。
引用访问对象,主流的访问方式有两种:使用句柄和直接指针。

如果使用句柄访问,Java堆中会划分一块内存作为句柄池,引用中存储的是对象的句柄地址。句柄中包含了对象实例数据和类型数据(方法区)各自的具体地址信息。
如果使用直接指针访问,必须考虑如何放置访问类型数据的相关信息。(只有类型数据指针,少一个到对象实例数据的指针)
句柄访问好处是:在对象被移动时,只会改变句柄中的“实例数据”指针,引用本身不被修改。

使用指针访问好处:速度更快,节省了指针定位的时间开销,Sun HotSpot而言,使用这种。
在单线程下,无论是栈帧太大还是VM栈容量太小,内存无法分配时,虚拟机抛出都是stackoverflow异常。

每个线程分到的栈容量越大,可以建立的线程数量就越少。如果是建立过多线程导致的内存溢出,通过减少最大堆和减少栈容量获取更多的线程。

2.4.3常量池溢出
outofMemory后面跟permgen space,说明运行时常量池属于方法区

2.4.4方法区溢出
cglib是字节码技术,增强的类越多,需要越大的方法区保证动态生成的class加载入内存。

2.4.5本机直接内存溢出
使用DirectByteBuffer,unsafe.allocateMemory()分配内存会抛出内存溢出异常。

你可能感兴趣的:(JVM)