jvm对每一个java程序员来说都非常重要,了解并掌握jvm可以帮助我们对java语言更好的掌控。
第一篇先记录一下关于jvm的基础知识
1、java内存区域
java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,如下图。
方法区:存放着已加载的类的信息、常量、静态变量、JIT编译器编译后的代码,方法区一般也成为永久带(pern Gen),该区域为线程共享的。
虚拟机栈:虚拟机栈是描述java方法执行的内存模型,在jvm中一个方法的执行过程就是这个虚拟机栈中的一个栈帧,每个栈帧中有局部变量表,操作栈,动态链接,方法出口等信息,该区域为线程独立的,生命周期与线程一致。
本地方法栈:与虚拟机栈功能类似,只不过是用于执行本地方法(native method)的。
堆:这应该是每一个java程序猿都应该掌握的内存区域,几乎所有的对象实例都会在这里分配内存,包括数组,堆也是java垃圾收集器主要工作的地方,一般堆会根据内存回收的策略而分成更细的几部分,新生代,老生代,新生代又可分为eden区域,from survivor区域和to survivor区域,该区域线程共享。
程序计数器:保存着jvm当前需要执行的指令,该区域线程独立。
运行时常量池:常量池属于方法区的一部分,具备动态性,java语言不要求常量一定是在编译器产生,比如String.intern()方法。
直接内存:不属于虚拟机的运行时数据区,在NIO中有体现,即DirectByteBuffer
2、内存溢出和内存泄露
内存溢出和内存泄露本质都是由于没有足够的内存分配而导致的错误。
内存泄露是由于有些对象已经不该存活,但是因为代码原因(如静态变量引用)等让这些对象无法被正常GC,导致最后没有足够的内存可以分配。
内存溢出则是应该存活的对象过多,导致的没有足够的内存分配。
虚拟机中的内存溢出包括以下几部分:
堆溢出:在堆中分配了大量的对象,且让这些对象一直存活,可以导致堆溢出。
虚拟机栈和本地方法栈溢出:当方法递归过多或者开启了过多线程(因为这部分内存是线程独立的)则会导致虚拟机栈和本地方法栈溢出,可以通过减小最大堆或者减小栈容量来换取更多的线程。
常量池溢出:没什么好说的,就是常量过多会导致。
直接内存溢出:同理。
3、关于GC的一些基础概念
GC(garbage collection),就是对一些不再被程序运行需要的内存进行回收。
如何判断一个对象已死?
在java堆中几乎存放着java世界中所有的实例,所以垃圾收集大部分都是在堆中进行,要回收一个对象必须要先判断是否存活,jvm虚拟机通过根搜索算法判断对象是否存活,
基本思路就是通过一系列名为“GC Roots”的对象作为起始点,对这些节点开始向下搜索,搜索走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连,则证明次对象是不可达的,即会被jvm判定会可回收对象。
可作为GC Roots的对象
虚拟机栈中的引用的对象(本地变量,如在一个方法中引用的一个对象)
方法区中的类静态属性引用的对象(一个类的静态变量引用的对象)
方法区中的常量引用的对象
本地方法栈中JNI引用的对象(native方法)
java中的四种引用(强软弱虚)
强引用:最普遍的一种引用,类似”Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收这部分内存
软引用:用于描述一些还有用,但并非必须存在的对象,如果jvm即将抛出内存溢出异常时,会对软引用的对象进行回收,如果这次回收还是没有足够的内存,才会抛出内存溢出异常,一般缓存框架中会使用软引用来存放缓存的对象,JDK中的SoftReference类来实现软引用
弱引用:弱引用也是描述并非必须存在的对象,但是它的强度比软引用更弱一点,它只能生存到下一次垃圾回收发生之前,当垃圾收集器工作时,弱引用的对象必定被回收掉,
jdk中提供了WeakReference类实现。
虚引用:最弱的引用关系,为一个实例设置虚引用仅仅是希望这个对象被回收时得到一个系统通知。
finalize方法
当一个对象被判断为不可达时,那么它会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。对象没有覆盖finalize方法或这个方法已被掉用过,则会被视为没有必要执行。
所以可以通过重写finalize方法来拯救对象,但是并不鼓励这样做,因为它的运行代价高昂,不确定性大。
方法区回收
很多人认为方法区没有垃圾回收,因为它被称为永久代,但其实是不对的,方法区也是有垃圾回收的,主要包括以下两部分。
1、回收废弃的常量
2、回收无用的类
判断一个常量是否被废弃很简单,和判断对象是否可达是一致的
而判断一个类是否无用需要满足以下三个条件
该类所有的实例都被回收
加载该类的classLoader已经被回收
该类对应的java.lang.Class对象没有在任何地方呗引用,无法在任何地方通过反射访问该类的方法。