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

运行时区域

d44e0f933c47f052ed5137b50998b9e7.png

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的角度看内存管理

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

从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对象的情况。通常都是由于内存泄露导致堆栈内存不断增大,从而引发内存溢出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

极域课堂管理系统软件如何取消控制_微缔电子组装业MES系统软件六大功能组成...

电子组装业MES系统软件六大功能组成MES系统软件是制造执行系统的英文简称,MES系统软件在整个企业信息集成系统中承上启下,是生产活动与管理活动信息沟通的桥梁,MES系统软件在产品从工单下发到生产成成品的整个过程中,扮演着促进生…

多个cuda 被单进程沾满_报名 | 提高GPU利用率,听英伟达专家分享这个CUDA工具

随着 NVIDIA GPU 计算性能的不断提升,如何提升 GPU 利用率是开发者普遍关心的问题之一。从 Kepler 架构开始,NVIDIA GPU 支持多个 CUDA kernels 函数的并发执行,称为 Hyper-Q 技术。Hyper-Q 技术支持多个 CUDA streams、多个 CPU threads 或者…

usb转ttl模块与matlab,USB接口转TTL小板的自检测试

现在电脑基本上都不会配置DB9串行数据端口了,这给一些喜欢折腾刷机和单片机加载程序的朋友带来了诸多的不便。还好,随着技术的发展,USB接口转TTL的产品越来越成熟,而这种产品主要以采用PL-2303HX芯片作为主控器的居多,…

matlab 误差椭圆,求3倍标准差误差椭圆分析的程序

根据《白话空间统计之九:方向分布(标准差椭圆)修正版》(有些地方没有理解清楚),写了下面的程序。但是好像结果不对Zmvnrnd([0.5 1.5], [0.025 0.03 ; 0.03 0.16], 50);XZ(:,1); YZ(:,2);mean_Xnanmean(X); mean_Ynanmean(Y); %椭圆圆心%确定长短半轴…

java ee cdi_Java EE CDI处理程序方法示例

java ee cdi这是CDI Disposer方法的教程。 在CDI中,由于Producer方法生成的对象随后可以注入到应用程序中,因此使用Disposer方法,以便在其工作完成时将其删除。 Disposer方法始终与Producer方法匹配。 Disposer方法使用的一个示例是当应用程…

python皮卡丘编程代码_再接再厉,用python编程13行代码解方程组(纯字符)

因为是示例为主,我们将方程组限制在二元一次方程组:x,y两个变量,两个方程。类似这样:每个方程有两个变量,x和y,形式为:axbycd由于这次有了两个方程,我们提取参数的代码就适合提炼为一…

快速提示:使用Chrome开发工具调试GWT应用程序

调试是软件开发的重要方面。 拥有正确的工具可以节省大量时间和头痛。 在GWT Super Dev模式之前,经典的Dev模式允许使用JVM调试。 开发人员可以在其IDE中设置断点,并使用调试模式来跟踪错误和错误。 现在,在超级开发模式下,情况有…

用matlab做纹理合成,关于图像纹理合成的Matlab例程

纹理是普遍存在的视觉现象,其可以描述地形、植物、矿石、纤维和皮肤等等物体的表面特征。纹理结构在图像中反映其图像像素取值的空间变化情况,这种变化具有某中统计规律,在纹理区域内的各部分具有大致相同的结构。纹理合成是利用计算机产生纹…

matlab评估边缘检测性能,【模糊推理】模糊逻辑图像边缘检测,原理+matlab代码~...

这篇博客是接着上一篇来哒,https://blog.csdn.net/luolan9611/article/details/94285158本篇博客及上篇博客搜集的资料、实验代码、实验报告、PPT均已上传至百度网盘:链接:https://pan.baidu.com/s/1AmT4TtBAxj1FKf4KUFcsBw 提取码&#x…

qt中实现左右分割线_Qt项目中,实现屏幕截图并生成gif的详细示例(值得细读)...

总第50篇平时我们在工作和学习的过程中,有时需要将桌面的某些动作截图生成gif动图,以更生动地呈现出来。目前有很多这样的软件,并且方便易使用,比如我经常使用的GifCam,软件小巧,生成的图片文件也比较小&am…

构建Spring Boot RESTful服务+ Spring Boot执行器

总览 什么是REST? REST(代表状态转移)是Web构建的体系结构样式,已成为用于Web应用程序的标准软件设计模式 。 代表性国家转移一词最早由REST的发起人,HTTP规范的主要作者之一Roy Fielding在其博士论文中使用 。 REST上…

tf调不到keras怎么 回事_格力变频空调快速维修方法及技巧 空调压缩机不到一分钟就停,怎么回事?...

格力变频空调快速维修方法及技巧一、 室内部分: 1、F1开路:制冷时不启动或启动一下就停机;制热正常,且一直高频运转。 2、F2开路:工作6—10分钟就停机,显示E2停止外机。 3、F2短路&#xff1…

php自动释放mysql连接,php怎么关闭mysql连接

php怎么关闭mysql连接2021-03-17 07:45:43php中可使用mysqli_close()函数来关闭mysql连接,语法格式“mysqli_close(connection);”。mysqli_close()函数可关闭先前打开的数据库连接,如果成功返回TRUE,反之则返回FALSE。本教程操作环境&#x…

Java 8 –按值对HashMap进行升序和降序排序

在上一篇文章中,我向您展示了如何通过键对Java 8中的Map进行排序 ,今天,我将教您如何使用Java 8功能(例如,lambda表达式,方法引用,流和新方法) 按值对Map进行排序。添加到java.util.…

scrcpy投屏_scrcpy 使用教程:将安卓设备投屏到 PC 端

阿拉平平读完需要6分钟速读仅需 2 分钟scrcpy 是一款开源的安卓设备投屏工具,通过 USB 或 Wi-Fi 与设备连接后就可以在 PC 端操作安卓设备,无需 root 权限且支持多平台运行。本文将演示如何使用 scrcpy 进行投屏操作。1. 下载安装到 Releases 下载最新的…

打砖块小游戏php程序,利用原生js实现html5打砖块小游戏(代码示例)

本篇文章给大家通过代码示例介绍一下利用原生js实现html5打砖块小游戏的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。前言PS:本次项目中使用了大量 es6 语法,故对于 es6 语法不太熟悉的小伙伴最好能先了…

si9000阻抗匹配计算_如何在设计之初计算出两层PCB板差分线的阻抗,线宽,间距...

最近在设计一款两层板PCB。板上一些高速信号线,分别是MIMP接口的差分线和USB2.0的差分线。既然是高速线,那么就需要设计成阻抗匹配走线。MIMP差分线需要做100ohm匹配,USB线需要做90ohm匹配。差分线阻抗的计算主要跟线宽,间距&…

jax-ws cxf_Apache CXF – JAX-WS –简单教程

jax-ws cxf许多Java开发人员都认为Web Service实现的任务艰巨-好吧,没有人能真正责怪他们,尤其是在企业应用程序开发的多年中,这给开发和设计带来了很多复杂性。 对于某些人来说,了解它是构建完整的企业应用程序的下一步-Web服务-…

写屏障是什么_面试官为什么问内存模型总离不开final关键字,该如何应对?

Java 语言的每个关键字都设计的很巧妙,金雕玉琢,只有深度钻研其中,才知其中懊悔,本文带领大家一起深入理解 Java 内存模型之 final。加我微信好友的不要着急,手机没电了,等我借个充电器之后,再一…

非静态方法可以访问Java中的静态变量/方法吗?

“非静态方法可以访问静态变量或调用静态方法”是Java中有关静态修饰符的常见问题之一,答案是, 是的 ,非静态方法可以访问静态变量或调用静态方法。 Java中的方法。 这没有问题,因为有静态成员,即静态变量和静态方法都…