Java内存区域与内存溢出异常(一)

11 篇文章 0 订阅

前言:

开了新坑,读书笔记了又。《深入理解java虚拟机》。慢慢坚持下去吧,看砖头书很难,但没办法,顶到既,订到既。

基本概念

java虚拟机运行时数据区:方法区、虚拟机栈、本地方法栈、堆、程序计数器

线程共享内存:方法区、堆

线程私有内存:虚拟机栈、本地方法栈、程序计数器

那么到底什么是线程私有和共享呢?

解答:各个线程之间该模块具有独立的内存空间,各个线程中此模块互不影响、独立存储则为线程私有,反之则共享。

例如那我们最熟悉的方法区和堆,方法区存储的是已加载的类信息、常量、静态变量,堆存储的是几乎所有对象实例。那么这些东西在不同线程中都是相同可复用的,根本不需要线程私有独立拥有。而反观程序计数器,存储的是当前线程所执行的字节码的行号指示器(其实就是记录方法执行到哪一句代码),这个明显在不同的线程中,就算执行相同的代码不可能都执行到哪里都是一样的,所以需要私有化,每个线程都有不同的程序计数器,记录代码的执行历程。

程序计数器:

程序计数器是一块比较小额度内存空间,他可以看错是当前线程所执行的字节码行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

需要记住的是如果执行的是native方法,则这个计数器为空(undefined)。

什么是native方法是?

native方法是指不用java语言写的方法,即底层用c++或者其他语言写的方法。

此内存区域是唯一一个在java虚拟机规范中没有规范任何OutOfMemoryError情况的区域。(内存溢出)

Java虚拟机栈

与程序计数器一样,java虚拟机栈也是线程私有的,它的生命周期和线程相同。虚拟机栈描述的是java方法执行的内存模型,每个方法在执行的时候同辉创建一个栈帧用于存储局部表量表、操作数栈、动态链接。每一个方法从调用直至到完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。

Java虚拟机栈中的局部表量表部分:存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型),其中64位长度的long和double类型的数据会占用两个局部变量空间,其他类型只占用一个。局部变量表所需的内存空间在编译期间完成分配。当进入一个方法中时。这个方法在帧中分配多大的局部变量空间是确定的,在方法运行期间不会改变局部变量表的大小。
对于这个区域Java虚拟机规定了两种异常情况:

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowErrpr异常;
  • 如果虚拟机栈可以动态扩展(不同虚拟机不同,大部分允许动态扩展),如果扩展的时候无法申请到足够的内存,就会抛出OutOfMemoryError异常

本地方法栈

与虚拟机栈发挥的作用相似,只不过虚拟机栈为虚拟机执行java方法(字节码.class文件)服务,而本地方法栈则为虚拟机适用native方法。native方法在上方已经解释过了。

既然和虚拟机栈相同则也会抛出上述两种异常StackOverflowErrpr异常、OutOfMemoryError异常

Java堆

Java堆时Java虚拟机所管理的内存中最大的一块。Java堆时被所有线程共享的一块区域,此内存区域的唯一母的是存储对象实例,几乎所有的对象实例都在这里分配

java堆也是垃圾收集器管理的主要区域,因此很多时候被称为“gc堆”,收集器基本采用分代收集算法,所以java堆中还可以细分为:新生代和老生带。Java堆可以处于无力不连续的内存空间中,只要逻辑上是连续的即可。

如果在堆中没有内存完成实例分配,而且堆也无法再扩展时,将会抛出OutOfMemoryErroe异常。

方法区

与java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即编译器编译后的代码等数据,java虚拟机规范把方法区描述为堆的一个逻辑部分,但却有一个别名:non-heap,目的是与java堆区分开来

方法区与java堆一样不需要连续的内存,可以选择固定大小或者可扩展。还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,这区域的内存回收目标主要针对敞亮吃的回收和对类型的卸载。

当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常。

方法区-运行时常量区:

是方法区的一部分。class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期间产生的各种字面量和符号引用,这部分内容将在类加载后进去方法区的运行时常量池中存放。

当常量池无法申请内存时时,抛出OutOfMemoryError异常。

直接内存:

直接内存不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但是这部分的内存也被频繁地使用,并且也可能导致OutOfMemoryError异常出现,在jdk1.4中加入了nio类,引入了一种基于通道与缓冲区的i/o方式,他可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆中的directbytebuffer对象作为这块内存的引用进行操作。

本机的直接内存分配不会收到java堆大小的限制,但是会收到本机总内存大小以及处理器寻址空间的限制。如果服务器管理员在配置虚拟机参数的时候,忽略直接内存,使哥哥内存区域综合大于物理内存限制,从而导致动态扩张时出现OutOfMemoryError异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值