JVM的内存区域划分:
  jvm的内存区域分为5部分:程序计数器,虚拟机栈,本地方法栈,堆跟方法区。
  程序计数器,虚拟机栈,本地方法栈三部分是线程私有的,堆跟方法区是公共的。
1、程序计数器
  是一块较小的内存区域,用于记录当前线程运行的位置,可以看做是程序所执行的字节码的行号指示器。如果正在执行的是一个java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是native方法,则计数器值为undefined。该部分也是jvm中唯一不会内存溢出的区域。说白了,以jvm规范的设计来讲,就是想内存溢出,也是没有机会的。
2、虚拟机栈
  就是我们通常所说的栈内存。线程中每个方法执行的时候都会创建一个栈帧,用于记录局部变量表,操作数栈,动态链接,方法出口等信息。
  在这里,如果线程请求深度大于虚拟机所允许的深度,将抛出StackOveflowError异常;如果动态扩展时无法申请到足够的内存,将抛出OutOfMemoryError异常。
  例如:无限递归调用。会导致不停的往当前线程的栈中添加栈帧,总会有栈太大,占满栈内存的时候,就会跑出StackOverflowError。
  《深入理解java虚拟机》一书中讲到,实际上jvm在这里的规定有点问题,因为当栈内存不够的时候,是因为已经使用过多还是栈空间太小,本质上是同一件事的两种描述。就是栈帧无限叠加,就会导致占用内存不断上升,从而申请内存,如果扩展到足够大,就会无法申请到足够的内存。但书中言明,经测试,单线程下无论由于栈帧太大还是虚拟机容量太小,抛出的都会stackOverflowError。但如果不限制单线程,在采用多线程情况下,只要为每个线程的栈分配的内存较大,就很容易产生OutOfMemoryError,但这跟jvm关于栈空间是否足够大的定义略有偏差。
出现StackOverflowError异常时,因为有错误堆栈信息可以阅读,相对来说较容易找到问题所在。而且使用虚拟机默认参数下,栈的深度大多数情况下达到1000-2000是很轻松的,对于正常的方法调用(包括真常递归)是足够的。但如果建立过多线程导致内存溢出,则需要考虑减少线程数量和更换64位虚拟机。若不能做到以上两者,则需要减少最大堆和减少栈容量,以换取更多的线程。
3、本地方法栈
  所发挥的作用跟虚拟机栈一样,只不过是jvm调用native方法时的栈而已。jvm规范对该部分所使用语言,使用方式以及数据结构并没有强制规定,因此不同的jvm实现是不一样的,甚至有的直接跟虚拟机栈合二为一(比如我们多数使用的HotSpot)。一样会抛出虚拟机栈的两种异常。
4、堆
  就是我们平常所说的堆内存,该部分主要用途就是存放对象实例。jvm规范中的描述是:所有的对象实例以及数组都要在堆上分配;但随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配在堆上也不是那么“绝对”了。
这也是垃圾回收的主要区域,可以通过-Xmx跟-Xms控制大小。如果堆中没有内存完成实例分配,而且也无法扩展时,抛出OutOfMemoryError.
5、方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。这里也是我们通常所说的“永久代”。但实际上并不是进入该区域的对象就真的永久存活了,这里仍然有相应的垃圾回收机制,只是相对并不频繁;而且此区域的内存回收目标主要是针对常量池的回收和对象类型的卸载。同样会有OutOfMemoryError
6、直接内存
对,除了jvm规范中定义的以上5个区域,还有一块区域是被频繁使用的。jdk1.4中加入了new Input/Output类,引入了一种基于通道与缓冲区的IO方式,就是jdk的新io中的各种reader,writer,inputstream,outputstream之类的。如果该部分内存跟虚拟机各个内存区域之和大于物理内存容量,同样会出现OutOfMemoryError。
对象的创建跟访问:
  当虚拟机遇到new指令的时候,首先将去查询这个指令的参数是否能在常量池中找到这个类的符号引用,并检查这个符号引用是否被加载、解析跟初始化过。若没有,必须先执行相应的类加载过程。
  1、类加载检查通过后,将对新生对象分配内存。对象所需的内存大小,在类加载完成后就可以完全确定。分配空间就是把一块儿确定大小的内存从java堆中划分出来。分配新内存有两种方式:“指针碰撞”跟“空闲列表”。
指针碰撞:用过的内存放一边,没用过的放一边,new的时候,指针移动一定距离,用来创建新对象。
空闲列表:用过的跟没用过的混在一起,但有一个列表记录着哪些用了哪些没用,new的时候从列表找一块儿足够大的划分给对象实例,并更新列表。
选择使用哪种方式,是由java堆是否规整决定的,而java堆是否规整是由于所采用的垃圾收集器是否带有压缩整理功能决定的。因此:Serial,ParNew等有压缩功能的垃圾收集器一般使用指针碰撞,CMS这种标记清除算法的,通常是空闲列表。
  2、并发情况下的new操作,有两种方案:
  a、对分配空间的动作进行同步处理,一半虚拟机上采用CAS配上失败重试的方式保证更新的原子性。
  b、给每个线程分配一个缓冲区,每个线程在自己缓冲区new对象,满了再进行申请。可以设置是否采用该方式,默认是true;
对象的内存布局
  HotSpot虚拟机中,对象的的内存布局分为3部分:对象头,实例数据,对齐填充。
  对象头:包括2部分,一部分用于存储对象自身的运行时数据,如HashCode,gc分带年龄,锁状态,线程持有锁,偏向锁id,偏向锁时间戳等,该部分称为“Mark Word”;另一部分是类型指针,是用来确定这个对象是哪个类的实例用的,如果对象是数组,该部分还包括数组的长度;但由于对象的访问方式有句柄跟直接指针2种,因此有的虚拟机实现种没有类型指针这部分,这类虚拟机的该部分内容在句柄中。
至于句柄跟直接指针,句柄就是引用类型的变量指针执行句柄对象(句柄对象在句柄池中),句柄对象包含到对象实例的指针跟对象类型的指针两部分;直接指针就是变量指针直接指向变量对象,但变量对象的头信息中包含一个指向对象类型信息的类型指针;很显然,我们用的HotSpot使用的是后者。

java内存区域与内存溢出的更多相关文章

  1. 深入理解java虚拟机系列(一):java内存区域与内存溢出异常

    文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...

  2. 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域  

  3. 深入了解Java虚拟机(1)java内存区域与内存溢出异常

    java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有,用于存储当前所执行的指令位置 2.Java虚拟机栈:线程私有,描叙Java方法执行模型:执行方法时都会创建一个栈帧,存储局 ...

  4. 深入理解java虚拟机---->java内存区域与内存溢出异常

    2. java内存区域于内存溢出异常 2.1 概述: 对于C/C++而言,内存管理具有最高的权利,既拥有每一个对象的“所有权”,又担负着每一个对象生命开始到结束的维护责任. 对于java而言,则把内存 ...

  5. 第二章Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...

  6. 2.1 自动内存管理机制--Java内存区域与内存溢出异常

    自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存 ...

  7. 虚拟机--第二章java内存区域与内存溢出异常--(抄书)

    这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...

  8. 深入理解Java虚拟机之Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  9. 深入理解Java虚拟机之图解Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  10. JVM内存区域与内存溢出异常

    Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常. >>运行时数据区域的划分 (1)程序计数器程序计数器(Progra ...

随机推荐

  1. 海量推荐系统:mapreduce的方法

    1. Motivation 2. MapReduce MapReduce是一种数据密集型并行计算框架. 待处理数据以"块"为单位存储在集群机器文件系统中(HDFS),并以(key, ...

  2. maredit测试

    int main() { } void addTotail(ListNode *& pHead, int value) { ListNode *node = new ListNode(); n ...

  3. 关于 node-sass 安装失败的问题

    最近换了一台计算机,使用ionic+cordova 开发,过程中发现node-sass 总是安装失败,经过搜索之后,发现可能是由于国内被墙导致的,找到了解决办法记录一下: 使用taobao 的镜像,成 ...

  4. Xamarin.Forms(一) 学习笔记

    Xamarin.Forms是Xamarin跨平台开发app的跨平台的一个Framework,要使用这套Framework,要从XAML说起. XAML是同通过xml的方式来描述控件和动作,可以通过编译 ...

  5. 使用KubeAdm部署Kubernetes集群——如何访问google代码仓库及Yum源

    一.申请国外服务器,部署ShadowSock服务 1.下载并创建配置文件 wget https://github.com/shadowsocks/shadowsocks-go/releases/dow ...

  6. 通过pip3安装virtualenvwrapper

    pip3 install virtualenvwrapper 配置virtualenvwrapper创建虚拟环境的目录和指定python3版本 环境编辑当前用户配置变量 mkdir ~/.virtua ...

  7. 微信小程序小结(4) -- 分包加载及小程序间跳转

    分包加载 某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载(主要是空间不够用,哈哈~). 在构建小程序分包项目时,构建会输出一个或多个功能的分包,其中 ...

  8. VIRT(虚拟内存)RES(常驻内存)和SHR(共享内存)

    VIRT: 1.进程“需要的”虚拟内存大小,包括进程使用的库.代码.数据,以及malloc.new分配的堆空间和分配的栈空间等: 2.假如进程新申请10MB的内存,但实际只使用了1MB,那么它会增长1 ...

  9. Step by Step: 基于MFC下的COM组件开发-Helloworld

    http://blog.csdn.net/sybifei/article/details/45008745 [这篇文章有问题, 仅供参考] http://blog.csdn.net/define_us ...

  10. POJ1008 Maya Calendar

    题目来源:http://poj.org/problem?id=1008 题目大意: Maya人认为一年有365天,但他们有两种日历.一种叫做Haab,有19个月.前18个月每月20天,每个月的名字分别 ...