运行时区域

1.程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机概念模型里(概念模型,各种虚拟机可能会通过一些更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令:分支、跳转、循环、异常处理、线程恢复等基础操作都会依赖这个计数器来完成。每个线程都有独立的程序计数器,用来在线程切换后能恢复到正确的执行位置,各条线程之间的计数器互不影响,独立存储。所以它是一个“线程私有”的内存区域。此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。2.虚拟机栈

JVM栈是线程私有的内存区域。它描述的是java方法执行的内存模型,每个方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,都对应着一个栈帧从入栈到出栈的过程。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法。就像是组成动画的一帧一帧的图片,方法的调用过程也是由栈帧切换来产生结果。
很多开发人员会把Java内存分为堆内存(Heap)和栈内存(Stack),这种划分的流行只能说明大多数开发人员最关注、与对象内存分配关系最密切的内存区域是这两块,其中所指的“堆”在后面会讲到,而所指的“栈”就是JVM栈,或者说是JVM栈中的局部变量表部分。实际上Java内存区域的划分远比这要复杂。局部变量表存放了编译器可知的各种基本数据类型(int、short、byte、char、double、float、long、boolean)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一跳字节码指令的地址)。
在JVM规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

3.本地方法栈

本地方法栈和虚拟机栈所发挥的作用是很相似的,它们之间的区别不过是 虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。Sun HotSpot 直接就把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。4.Java堆

对于大多数应用来说,Java堆(Heap)是JVM所管理的内存中最大的一块。它是被所有线程共享的一块内存区域,在虚拟机启动时创建。主要用来存放对象实例,所有的对象实例以及数组都要在堆上分配。堆是垃圾收集器管理的主要区域,也被称为“GC堆”,从内存回收的角度来看,堆可以细分为:新生代和老年代;再细致一点可分为:Eden空间、From Survivor空间、To Survivor空间(空间分配比例是8:1:1)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将抛出OutOfMemoryError异常。5.方法区

方法区(Method Area)与堆一样,也是各个线程共享的区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,也就是用来存储类的描述信息—元数据的。方法区是堆的一个逻辑部分,为了区分Java堆,它还有一个别名Non-Heap(非堆)。相对而言,GC对于这个区域的收集是很少出现的。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
在Java 7及之前版本,我们也习惯称它为“永久代”(Permanent Generation),更确切来说,应该是“HotSpot使用永久代实现了方法区”。需要注意的是,从Java 8开始,“永久代”已经被彻底移除,使用了一个元空间(Metaspace)来代替它,后面我们会详细讲解

6.运行时常量池

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池( Constant pool table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入运行时常量池中存放。运行时常量池相对于class文件常量池的另外一个特性是具备动态性,java语言并不要求常量一定只有编译器才产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。
在近三个JDK版本(1.6、1.7、1.8)中, 运行时常量池(Runtime Constant Pool)的所处区域一直在不断的变化,在JDK1.6时它是方法区的一部分;1.7又把他放到了堆内存中;1.8之后出现了元空间,它又回到了方法区。其实,这也说明了官方对“永久代”的优化从1.7就已经开始了。7.直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是JVM规范中定义的内存区域。但这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现。它在JDK中最直观的表现就是NIO,基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

从GC的角度看内存管理

年轻代、老年代、方法区(老年代由元空间替换)

从Java8开始,HotSpot已经完全将永久代(Permanent Generation)移除,取而代之的是一个新的区域—元空间(MetaSpace),它使用本地内存来存储类元数据信息。也就是说,只要本地内存足够,它不会出现像永久代中“java.lang.OutOfMemoryError: PermGen space”这种错误。同样的,对永久代的设置参数 PermSize 和 MaxPermSize 也会失效。默认情况下,“元空间”的大小可以动态调整,或者使用新参数MaxMetaspaceSize 来限制本地内存分配给类元数据的大小。

为什么叫元空间?是因为这里面存储的是类的元数据信息。

元数据(Meta Date),关于数据的数据或者叫做用来描述数据的数据或者叫做信息的信息。这些定义都很是抽象,我们可以把元数据简单的理解成,最小的数据单位。元数据可以为数据说明其元素或属性(名称、大小、数据类型、等),或其结构(长度、字段、数据列),或其相关数据(位于何处、如何联系、拥有者)。

为什么要移除永久代,用元空间取代呢?

1.静态变量将会从永久代移除, 放入Java heap或者native memory. 其中建议JVM的实现中将类的元数据放入 native memory, 将字符串池类的静态变量放入Java堆中. 这样可以加载多少类的元数据就不在由MaxPermSize控制, 而由系统的实际可用空间来控制.

2.为什么这么做呢?

  • 字符串存在永久代中,容易出现性能问题和内存溢出。
  • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  • 减少OOM只是表因, 更深层的原因还是要合并HotSpot和JRockit的代码, JRockit从来没有一个叫永久代的东西, 但是运行良好, 也不需要开发运维人员设置这么一个永久代的大小.

元空间特色

  • 充分利用了Java语言规范:类及相关的元数据的生命周期与类加载器的一致。
  • 每个类加载器都有它的内存区域-元空间
  • 只进行线性分配
  • 不会单独回收某个类(除了重定义类 RedefineClasses 或类加载失败)
  • 没有GC扫描或压缩
  • 元空间里的对象不会被转移
  • 如果GC发现某个类加载器不再存活,会对整个元空间进行集体回收

GC

  • Full GC时,指向元数据指针都不用再扫描,减少了Full GC的时间。
  • 很多复杂的元数据扫描的代码(尤其是CMS里面的那些)都删除了。
  • 元空间只有少量的指针指向Java堆。这包括:类的元数据中指向java.lang.Class实例的指针;数组类的元数据中,指向java.lang.Class集合的指针。
  • 没有元数据压缩的开销
  • 减少了GC Root的扫描(不再扫描虚拟机里面的已加载类的目录和其它的内部哈希表)
  • G1回收器中,并发标记阶段完成后就可以进行类的卸载

元空间内存分配模型

  • 绝大多数的类元数据的空间都从本地内存中分配
  • 用来描述类元数据的对象也被移除
  • 为元数据分配了多个映射的虚拟内存空间。
  • 为每个类加载器分配一个内存块列表。
    • 块的大小取决于类加载器的类型
    • Java反射的字节码存取器(sun.reflect.DelegatingClassLoader )占用内存更小
  • 空闲块内存返还给块内存列表
  • 当元空间为空,虚拟内存空间会被回收
  • 减少了内存碎片

异常
在JVM规范的描述中,除了程序计数器以外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError的可能。
运行时区域异常主要原因:

  • 虚拟机栈和本地方法栈 StackOverflowError、OutOfMemoryError StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度;OutOfMemoryError:虚拟机在扩展栈时无法申请足够的内存空间 。
  • 堆 OutOfMemoryError: 对象数量到达最大堆的容量,内存泄漏、内存溢出
  • 方法区和运行时常量池 OutOfMemoryError: 反射,动态代理:CGLib、JSP、OSGI等
  • 内存泄露(Memory Leak):程序在申请内存后,对象没有被GC所回收,它始终占用内存,内存泄漏的堆积最终会造成内存溢出。
  • 内存溢出(Memory Overflow):程序运行过程中无法申请到足够的内存而导致的一种错误。内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。通常都是由于内存泄露导致堆栈内存不断增大,从而引发内存溢出。

运行时错误7内存溢出_分别从运行时和GC的角度看JAVA8内存管理相关推荐

  1. ART运行时Foreground GC和Background GC切换过程分析

    通过前面一系列文章的学习,我们知道了ART运行时既支持Mark-Sweep GC,又支持Compacting GC.其中,Mark-Sweep GC执行效率更高,但是存在内存碎片问题:而Compact ...

  2. for循环十万条数据内存溢出_强如 Disruptor 也发生内存溢出?

    前言 OutOfMemoryError 问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界.空指针等)来说这类问题是很难定位和解决的. 本文以最近碰到的一次线上内存溢出的定位.解决问题的方式展开 ...

  3. node aws 内存溢出_如何使用Node.js和AWS快速创建无服务器RESTful API

    node aws 内存溢出 by Mark Hopson 马克·霍普森(Mark Hopson) 如何使用Node.js和AWS快速创建无服务器RESTful API (How to quickly ...

  4. arthas 排查内存溢出_【spark-tips】spark2.4.0触发的executor内存溢出排查

    版本升级背景 spark 2.4.0 最近刚发版,新增了很多令人振奋的特性.由于本司目前使用的是spark 2.3.0版本,本没打算这么快升级到2.4.0.无奈最近排查出的两个大bug迫使我们只能对s ...

  5. 内存溢出_容易造成单片机内存溢出的几个陷阱

    [小宅按] 关于程序变量和内存分配,都是需要我们时刻关注的问题.我相信有不少人在这块犯过很多的错误,也可能说明我们基础不够扎实,编写程序的习惯也不够好. 总结一下关于程序的变量和内存方面的概念,虽然是 ...

  6. java 二维数组内存溢出_模拟Java内存溢出

    本文通过修改虚拟机启动参数,来剖析常见的java内存溢出异常(基于jdk1.8). 修改虚拟机启动参数 这里我们使用的是IDEA集成开发环境,选择Run/Debug Configurations 然后 ...

  7. 内存溢出_关于PermGen Space内存溢出解决方案

    内存溢出分为Heap Space和PermGen Space两种异常.正巧的是我这次就碰到了PermGen space异常,为了解决这个异常花了我 半天的时间,所以今天写这个方案就是做一种笔记. 刚开 ...

  8. java什么时候可能产生内存溢出_哪些场景会产生OOM?怎么解决?

    Java技术栈 www.javastack.cn 关注优质文章 这个面试题是一个朋友在面试的时候碰到的,什么时候会抛出OutOfMemery异常呢?初看好像挺简单的,其实深究起来考察的是对整个JVM的 ...

  9. node aws 内存溢出_在AWS Elastic Beanstalk上运行生产Node应用程序的现实

    node aws 内存溢出 by Jared Nutt 贾里德·努特(Jared Nutt) 在AWS Elastic Beanstalk上运行生产Node应用程序的现实 (The reality o ...

最新文章

  1. maven打包记录1
  2. php bootstraptable分页,Bootstrap table分页问题汇总【附答案代码】
  3. 18、Page Object 设计模式
  4. Elasticsearch集群Red,分片无法恢复:cannot allocatebecause all found copies of the shard are either stale or c
  5. 【读书笔记】Android的Ashmem机制学习
  6. python怎样打开加密的文件_如何用Python 加密文件
  7. 怎么圆角变直角_衣柜设计个圆角有什么用?效果好看又实用,会这样装的都是老木工...
  8. php 水平测试,测试您的 PHP 水平的题目_PHP教程
  9. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画
  10. sqlserver服务启动后停止,传递给数据库 'master' 中的日志扫描操作的日志扫描号无效...
  11. 【AI视野·今日CV 计算机视觉论文速览 第233期】Tue, 3 Aug 2021
  12. Python将类对象转换为json
  13. Python3学习日志 函数参数
  14. Java基础之continue与break区别
  15. 计算机组成原理中断实验的分析,计算机组成原理_中断实验.pdf
  16. python实现进程调度算法
  17. 飞猪 Serverless 体系从无到有,落地10余个业务场景
  18. QT5实现职工工资信息管理系统(文件读写)
  19. pta森森快递(线段树 + 贪心 + 区间修改)
  20. java快捷键格式化_在Java中Format的快捷键是什么?

热门文章

  1. Devoxx 2011印象
  2. mysql设计一个简单的系统_一个简单数据库设计例子
  3. 计算机专业开学要带电脑吗,大学上课要带电脑吗
  4. JAVA8如何进行IDEA配置_IntelliJ Idea 配置jdk8
  5. http 和 https_HTTPS与HTTP区别
  6. 初学者宝典:C语言入门基础知识大全(中)
  7. C语言结构体字节对齐
  8. java8默认垃圾回收器,Java 8的默认垃圾收集器
  9. sentinel接入网关应用_接入容器服务Kubernetes版应用
  10. mysql batch 失败_mysql突然挂掉,重启失败,求助