深入理解JVM虚拟机读书笔记【第二章】Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常
2.2 - 运行时数据区域
2.2.1 - 程序计数器
★作用:
1.用来标记线程执行到哪些方法了。【字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码执行,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。】
★特点:
1.每个线程独有自己的程序计数器。放在线程私有的内存中。
2.如果线程执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是本地(Native)方法,这个技术器的值则为空(Undefined)。
3.此区域是没有规定任何OutOfMemoryError情况的区域。
2.2.2 - Java虚拟机栈
★作用:
1.用来存储Java方法执行过程中的一些信息的,如:局部变量表、操作栈、动态链接、方法出口等信息。
★特点:【局部变量表】
1.也是每个线程私有的,生命周期与线程相同
2.每个方法被调用直至执行完成的过程,就对于着一个栈帧在虚拟机栈中从入栈到出栈的过程。
3.Java虚拟机规范对这个区域规定了2中异常
1.StackOverflowError异常:如果线程请求的栈的深度大于虚拟机所允许的深度,则会抛出该异常。
2.OutOfMemoryError异常:当虚拟机栈可动态扩展时,(也有固定大小的选择),当扩展的时候,无法申请到足够的内存时会抛出该异常。
2.2.3 - 本地方法栈
★作用:
1.与Java虚拟机栈的作用非常相似。
2.区别就是,虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
★特点:
1.一样的会抛StackOverflowError异常和OutOfMemoryError异常
2.2.4 - Java堆
★作用:
1.存放对象实例的
★特点:
1.被所有线程共享的一块内存区域,在虚拟机启动时创建。
2.是垃圾收集器管理的主要区域。即可实现成固定大小的,也可以是扩展的。如果堆中没有内存完成实例分配,也无法再扩展时,会报OutOfMemoryError异常
3.可以处于物理上不连续的内存空间中。只要逻辑上是连续的即可,就像我们的磁盘空间一样。
2.2.5 - 方法区【别名非堆,为了和堆区分开来。亦是永久代,本质不等价】
★作用:
1.用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
★特点:
1.与Java堆一样,是各个线程共享的内存区域。
2.别名非堆,为了和堆区分开来。亦是永久代,本质不等价。因为虚拟机的设计团队用永久代来实现方法区。
3.垃圾收集行为在方法区是比较少出现的,但并不是数据在方法区就会一直存在。
4.方法区的内存回收目标主要是【方法区的回收成绩比较难以令人满意】
1.针对常量池的回收
2.对类型的卸载
5.在Sun公司的bug列表中的几个严重bug就是由于低版本的HotSpot虚拟机对方法区未完全回收而导致内存泄漏。
6.据规定,当方法区无法满足内存分配需求时,会抛OutOfMemoryError异常
2.2.6 - 运行时常量池
★作用:
1.Class文件中的常量池信息,在类加载后,会被放到方法区的运行是常量池中。
★特点:
1.是方法区的一部分。【来源可以是Class文件中的常量池信息,运行期间也可以将新的常量放入池中】
2.相比于Class文件中的常量池信息,Java虚拟机规范对运行时常量池没有做任何细节的要求。
3.相比于Class文件中的常量池信息,运行时常量池具备动态性。当无法再申请到内存的时候会抛出OutOfMemoryError异常
★额外点:
1.Class文件中有一项信息是常量池。Class文件中的常量池,用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后存放到方法区的运行是常量池中。
2.2.7 - 直接内存
★作用:
1.
★特点:
1.不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,使用频率很高,会报OutOfMemoryError异常
2.内存的分配不受Java堆大小的限制,受本机总内存大小及处理器寻址空间的限制。
★注意:
1.服务器管理员配置虚拟机参数时,一般会根据实际内存设置 -Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError异常。
2.3 - 对象访问
2.3.1 - 对象的访问方式
★句柄方式访问:
优点:
1.reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
直观:
★直接指针访问:【Sun HotSpot,是使用该方式访问对象】
优点:
1.速度快,节省了一次指针定位的时间开销.【因为对象的访问在Java中非常频繁,因此这类开销极少成多后也是一项非常可观的执行成本】
直观:
2.4 - 实战:OutOfMemoryError异常
2.4.1 - Java堆溢出
★问题分析
1.对dump出来的堆转储快照进行分析,确认对象中的内存是否是必要的。分析出是内存泄漏还是内存溢出。
2.如果是内存泄漏,进一步通过工具查看对象的GC Roots引用链。找到对象类型信息,找到代码相关位置。
3.不存在内存泄漏,尝试调大堆参数。检查对象的生命周期是否过长。
2.4.2 - 虚拟机栈和本地方法栈溢出
1.在单线程下无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常
2.如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。
2.4.3 - 运行时常量池溢出
2.4.4 - 方法区溢出
1.增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载入内存。
2.方法区溢出也是一种常见的溢出异常,一个类如果要被垃圾收集器回收掉,判定条件是非常苛刻的。
3.在经常动态生成大量Class的应用中,需要特别注意类的回收状况。(如:GCLib,大量JSP,动态生成JSP的文件应用)
4.基于OSGI的应用(即使是同一个类文件,被不同的加载器加载也会视为不同的类)。