本系列随笔是我在学习周志明的《深入理解JAVA虚拟机》一书时的内容整理。
不知不觉又有一个月没写随笔了,这段时间课程的实验和作业比较多,自己学习的时间比较少。而在老早之前我就知道关于JVM是必须要知道的,而本书又是各位前辈推荐的书,现在终于开始要跳出java基础(虽然并不觉得自己的实力够跳出来),接触一下底层虚拟机的技术了。
本次的内容整理从第二部分:自动内存管理机制的第二章:Java内存区域与内存溢出异常讲起。而由于自己的水平原因,我也会在之后的时间里对随笔进行增添和修改,希望自己有所收获!这一章讲的是JVM的基本组成和内存溢出异常。总的来说是这样的,我们都知道一个java源程序需要先经过编译生成.class文件,class字节码文件再由JVM运行(半编译)。而虚拟机技术并不只是java有,java中的虚拟机也并不是只有一种,sun公司(正统的)的虚拟机JVM就是hotspot,关于它的实现是C语言,可以在网上下到源代码。Java虚拟机会将所管理的内存分为以下几个运行时数据区域:线程共享的方法区和堆区,线程隔离的虚拟机栈、本地方法栈和程序计数器。
这里还要讲到一个概念:native方法。native方法指的是java调用非java代码的接口,而native方法的计数器值为空。虚拟机栈区即存放基本数据类型的地方,这个区域规定了栈溢出和内存溢出异常;本地方法栈区与虚拟机栈区类似,但它执行的是native方法,同样有两种溢出异常。堆区是所有对象实例及数组分配的地方,一般来说是内存中最大的一块,它是GC管理的主要区域,又称“GC堆”,可分为新生代和老年代,可通过-Xmx和-Xms参数来设置堆大小,这里有内存溢出异常。方法区:这里是存储已被虚拟机加载的类信息、常量、静态变量等数据(非堆),又称为“永久代”,但这个概念正在淡化,可用--XX:MaxPermSize来设置上限(在JDK1.7的HotSpot中已经把原本放在永久代的字符串常量池移出),也有内存溢出异常。另外,“运行时常量池”是方法区的一部分,它相对于Class文件常量池的一个重要特征是动态性(可在运行时放入新的常量:如intern方法)。直接内存并不是JVM运行时数据区的一部分,但也被频繁使用,可能会导致内存溢出异常。
下面是关于对象在JVM中的具体内容:
1.对象的创建
对象的创建首先通过类加载检查,接下来JVM会为新生对象分配内存,分配完后将分配的内存空间初始化为零值,最后对对象进行必要的设置(例如对象是哪个类的实例等等),注意此时init方法还没有执行,但jvm视角来说一个新的对象已经生成。
2.对象的内存布局分为三块:对象头、实例数据和对齐填充;对象的访问定位分为句柄和直接指针两种。这些具体概念不详述。
最后是总结的一些要点:
1.栈容量只由-Xxs参数设置
2.JDK1.7以前intern()方法会复制“永久代”中的实例,JDK1.7之后不会复制实例(即new的同一个)。
3.CGLib技术
4.对象实例数据(堆):对象中各个实例字段的数据;对象类型数据(方法区):对象的类型、父类、实现的接口、方法等;
静态区(也在方法区中)用来存放静态变量,静态块。