JVM模型及内存溢出

一、JVM截图及概念

图1:JVM虚拟机运行时数据区域概念模型

1、程序计数器:内存空间中的一块小区域,作为当前线程所执行的字节码的行号指示器,注:如果是native方法,计数器为空

2、虚拟机栈:线程私有,生命周期与线程相同,虚拟机栈描述的是Java方法执行的内存模型:创建栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息

3、本地方法栈:和虚拟机栈功能类似,虚拟机使用本地Native方法服务

4、Java堆:线程共享,用于存放对象,是GC的主要管理区域

5、方法区:线程共享,用于存储已被虚拟机加载的类信息,变量,静态变量,即时编译器编译后的代码等数据

6、运行时常量池:编译期生成的各种字面量和符号引用

7、直接内存:直接内存不属于虚拟机运行时数据区的一部分,但是这部分内存也会被频繁的使用,而且也会导致OutOfMemoryError的异常

在JDK1.4中引入的NIO类中,有一种基于通道和缓存区的I/O方式,可以使用native函数直接调用堆外内存,然后使用Java堆中的DirectByteBuffer对象来引用操作,可以提高JVM的性能

二、JVM中对象的创建

当JVM解析,遇到new指令时,JVM首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个类是否被加载、解析和初始化过。

如果没有,那必须先执行相应的类加载过程(加载,验证,准备,解析,初始化),在类加载检查通过后,JVM会对新生对象在Java堆中分配内存,内存的大小在类加载完成后才能够完全确定。Java内存的分配方式有:指针碰撞和空闲列表,他们的分配原则是根据JVM中堆是否规整决定的。

对象在内存中的布局可以分为3块区域:对象头(运行时数据【哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳】,类型指针【指向类元数据指针】)、实例数据和填充

三、JVM中对象的访问

在JVM中对象的访问主要使用:句柄和指针

图2:使用句柄访问对象

图3:使用指针访问对象

两种对象访问方式各有优势,使用句柄来访问的最大好处是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要修改。使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销。HotSpot虚拟机使用的是直接指针访问的方式。句柄来访问的情况也十分常见。

四、Java中出现OutOfMemoryError的情况

在图1的JVM虚拟机运行时数据区域概念模型中存在程序计数器、虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池、直接内存,除了程序计数器,其他的部分如果操作不当都会产生运行时内存溢出的问题,下面将对虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池、直接内存的溢出情况作阐述:

1、Java堆溢出

一般创建的对象在堆中容纳不下就会溢出,当抛出内存溢出问题时,需要进一步确定到底是内存泄露还是内存溢出,如果是内存泄露,那么进一步通过工具查看泄露对象到GC Roots的引用链,找到泄露代码的位置;如果是内存溢出,那么可以在物体机器中调大堆参数(-Xmx和-Xms),也需要检查代码中是否存在某些对象生命期过长、持有状态时间过程的情况,尝试减少程序运行期的内存消耗

2、虚拟机栈和本地方法栈溢出

虚拟机栈和本地方法栈溢出原因可能有以下两种:StackOverflowError异常和OutOfMemory异常

注:在单线程条件下,无论是请求的栈深度大于虚拟机所允许的最大深度,还是内存溢出,虚拟机都会抛出StackOverflowError异常,多线程会出现内存溢出异常

故:在多线程导致内存溢出,在不能减少线程数量和更换虚拟机的情况下,只能通过减少最大堆和减少栈容量来换取更多的线程

3、方法区和运行时常量池溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述,所以在产生大量动态类时,方法区就会出现溢出。例如CGLib字节码增强和动态语言以外,还有大量JSP或者动态产生JSP文件的应用和基于OSGI的应用

4、直接内存溢出:略

时间: 2024-09-28 07:18:51

JVM模型及内存溢出的相关文章

深入理解JVM阅读笔记-内存溢出小结

JAVA系统除了程序计数器和虚拟机内存之外的其它几个内存区域都有发生OutOfMemory(OOM)的可能.堆,栈,方法区,静态常量池,直接内存,都是可能的. 1.Java堆溢出 Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在堆达到最大容量的限制后就会产生内存溢出异常. -Xms -Xmx 参数可以设置Java堆的大小(实际使用中一般Xms和Xmx的大小一致,放置堆自动扩展),通过参数-XX:+HeapDumpO

JVM原理及内存溢出

深入理解jvm之内存区域与内存溢出

文章目录 1. Java内存区域与内存溢出异常 1.1. 运行时数据区域 1.1.1. 程序计数器 1.1.2. java虚拟机栈 1.1.3. 本地方法栈 1.1.4. Java堆(Java Heap) 1.1.5. 方法区 1.1.6. 运行时常量池 1.1.7. 直接内存 1.2. HotSpot虚拟机 1.2.1. 对象的创建 1.2.2. 对象的访问定位 1.3. OOM异常的解决思路 1.4. 参考 Java内存区域与内存溢出异常 运行时数据区域 程序计数器 当前线程所执行的字节码的

BAT面试必问题系列:深入详解JVM 内存区域及内存溢出分析

前言 在JVM的管控下,Java程序员不再需要管理内存的分配与释放,这和在C和C++的世界是完全不一样的.所以,在JVM的帮助下,Java程序员很少会关注内存泄露和内存溢出的问题.但是,一旦JVM发生这些情况的时候,如果你不清楚JVM内存的内存管理机制是很难定位与解决问题的. 一.JVM 内存区域 Java虚拟机在运行时,会把内存空间分为若干个区域,根据<Java虚拟机规范(Java SE 7 版)>的规定,Java虚拟机所管理的内存区域分为如下部分:方法区.堆内存.虚拟机栈.本地方法栈.程序

一次使用Eclipse Memory Analyzer分析Tomcat内存溢出

转:http://tivan.iteye.com/blog/1487855 前言 在平时开发.测试过程中.甚至是生产环境中,有时会遇到OutOfMemoryError,Java堆溢出了,这表明程序有严重的问题.我们需要找造成OutOfMemoryError原因.一般有两种情况: 1.内存泄露,对象已经死了,无法通过垃圾收集器进行自动回收,通过找出泄露的代码位置和原因,才好确定解决方案:2.内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小(-Xmx与-Xms),检

一次使用Eclipse Memory Analyzer分析Tomcat内存溢出(转)

前言 在平时开发.测试过程中.甚至是生产环境中,有时会遇到OutOfMemoryError,Java堆溢出了,这表明程序有严重的问题.我们需要找造成OutOfMemoryError原因.一般有两种情况: 1.内存泄露,对象已经死了,无法通过垃圾收集器进行自动回收,通过找出泄露的代码位置和原因,才好确定解决方案:2.内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小(-Xmx与-Xms),检查代码是否存在对象生命周期太长.持有状态时间过长的情况.以上是处理Java堆

深入理解JVM读书笔记一: Java内存区域与内存溢出异常

Java虚拟机管理的内存包括几个运行时数据内存:方法区.虚拟机栈.本地方法栈.堆.程序计数器,其中方法区和堆是由线程共享的数据区,其他几个是线程隔离的数据区. 2.2 运行时数据区域 2.2.1程序计数器 程序计数器是一块较小的内存,他可以看做是当前线程所执行的行号指示器.字节码解释器工作的时候就是通过改变这个计数器的值来选取下一条需要执行的字节码的指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成.如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚

JVM高级特性与实践(一):Java内存区域 与 内存溢出异常

对于从事C.C++的开发人员而言,在内存管理领域,他们具有绝对的“权利”——拥有每个对象的控制权,并担负着每个对象生命周期的维护责任.而对于Java开发人员而言,在虚拟机自动内存管理机制的帮助下,无需为每一个创建new操作去配对 delete/free 代码,减少内存泄漏和内存溢出的问题,这些都交给了Java虚拟机去进行内存控制,但是正因如此,当出现相关问题时,若不了解JVM使用内存规则,就难以排查错误.接下来以此篇文章记录学习Java虚拟机内存各个区域概念.作用.服务对象以及可能产生的问题.

JVM学习—内存方区域与内存溢出异常

Java与C++中存在的一堵高墙就是又内存动态分配和垃圾回收技术所围成的,墙外面的人想进去,墙里面的人想出去. Java将内存控制权交给了JVM,所以程序员无需为每个对象手动释放空间,所以不容易出现内存泄露与溢出.但是一旦出现内存泄露或溢出,如果不了解虚拟机怎样分配内存的,那么排查将非常困难.Java虚拟机将执行java程序过程中管理的内存划分成若干个区域,每个区域有各自的用途以及创建和销毁的时间.这些区域有的是随着JVM进程启动而存在,有的则是通过用户线程的启动和结束而建立和销毁. 程序计数器