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. 《Linux内核设计与实现》读书笔记(六)- 内核数据结构

    内核数据结构贯穿于整个内核代码中,这里介绍4个基本的内核数据结构. 利用这4个基本的数据结构,可以在编写内核代码时节约大量时间. 主要内容: 链表 队列 映射 红黑树 1. 链表 链表是linux内核 ...

  2. C#知识点总结系列:3、C#中Delegate和Event以及它们的区别

    1.Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 ...

  3. Http工作原理(转)

    HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议.它可以使浏览器更加高效,使网络传输减少.它不仅保证计算机正确 ...

  4. vue 路由里面的 hash 和 history

    对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义.前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求. ...

  5. 安卓手机牛逼软件Termux中安装Archlinux,安装Jdk

    说出来你可能不信,手机上居然装了两个linux系统,和真实的linux有些许些差别. 首先安装了Termux以后你发现,好多linux常用功能都有,什么Pyhton,gcc,g++,ruby,Php, ...

  6. 用户、组或角色 'zgb' 在当前数据库中已存在。 (Microsoft SQL Server,错误: 15023)

    在使用SQL Server 时,我们经常会遇到一个情况:需要把一台服务器上的数据库转移到另外一台服务器上.而转移完成后,需要给一个"登录"关联一个"用户"时,往 ...

  7. P1114 “非常男女”计划

    题意:给你一个01串,求满足0和1总数相等的最大字串   $n\ \le\ 10^5$ 1.$O(n^3)$枚举起点终点,统计判断是否成立 2.$O(n^2)$先$O(n)$时间计算01个数的前缀和, ...

  8. C语言中变量、全局变量与变量的作用域

    什么是变量: 变量常量都是表征数据的一种形式:常量用来表示数据的值: 变量不仅可以用来表示数据的值:还可以用来存放数据:因为变量对应着一定的内存单元: 变量和常量必须先定义后使用. 变量名和常量名都是 ...

  9. 浅谈c语言的线性表的基本操作———基于严蔚敏的数据结构(c语言版)

    主要讲的是线性表的创建,插入及删除: 0. 线性表的建立,对于这类操作主要是利用了结构体的性质,对于定义的线性表的特性主要有三点:首先 Typedef struct { ElemType   *ele ...

  10. 2016 Multi-University Training Contest 10 [HDU 5861] Road (线段树:区间覆盖+单点最大小)

    HDU 5861 题意 在n个村庄之间存在n-1段路,令某段路开放一天需要交纳wi的费用,但是每段路只能开放一次,一旦关闭将不再开放.现在给你接下来m天内的计划,在第i天,需要对村庄ai到村庄bi的道 ...