1 Java内存区域与内存溢出异常

九路
• 阅读 1111

1 java虚拟机对内存的管理

java虚拟机在执行java程序的时候把内存分为若干个不同的区,这些区各自有不同的用处,以及创建和销毁时间. 有的区随着虚拟机的启动而启动,有的区则依赖用户线程的启动和结束而启动和结束. 根据java虚拟机规范,java虚拟机将内存分为下面几个部分:如下图

1 Java内存区域与内存溢出异常

下面是对这些区的介绍 1 程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。字节码解释器就是通过改变该计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需依赖计数器来完成。 每一个JVM线程都有独立的程序计数器,各线程间的计数器互不影响,独立存储,确保线程切换后能够恢复到正确的执行位置。 在任意时刻,一条JVM线程只会执行一个方法的代码。该方法称为该线程的当前方法(Current Method),如果该方法是Java方法,那计数器保存JVM正在执行的字节码指令的地址;如果该方法是Native,那PC寄存器的值为空(Undefined)。 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2 Java虚拟机栈(Java Virtual Machine Stack)

Java虚拟机栈与程序计数器一样,也是线程私有的,其生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)。其中64位长度的long和double会占用2个局部变量空间(Slot),其余数据类型只占用1个。局部变量表所需的空间在编译期间完成分配,当进入一个方法时,其需要在帧中分配多大的局部变量空间是确定的,方法运行期间不会改变局部变量表的大小。
Java虚拟机规范中对该区域规定了两种异常情况:
1)如线程请求的深度大于虚拟机所允许的深度,抛出StackOverflowError异常。
2)虚拟机栈动态扩展无法申请到足够的内存时,抛出OutOfMemoryError异常。

3 本地方法栈(Native Method Stack)

Java虚拟机可能会使用到传统的栈来支持native方法(使用Java语言以外的其它语言编写的方法)的执行,这个栈就是本地方法栈(Native Method Stack)。本地方法栈与虚拟机栈非常类似,区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,二本地方法栈则为虚拟机使用到的Native方法服务。虚拟机规范对本地方法栈中的方法是用语言、使用方式与数据结构没强制规定,因此虚拟机可以自由实现,如Sun HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。 Java虚拟机规范中对该区域规定了两种异常情况: 1)如线程请求的深度大于虚拟机所允许的深度,抛出StackOverflowError异常。 2)虚拟机栈动态扩展无法申请到足够的内存时,抛出OutOfMemoryError异常。

4 Java堆(Java Heap)

Java堆是Java虚拟机管理内存中最大的一块,是所有线程共享的内存区域,随虚拟机的启动而创建。该区域唯一目的是存放对象实例,几乎所有对象的实例都在堆里面分配。Java堆是垃圾收集器管理的主要区域,被称作“GC堆”。 Java虚拟机规范规定,Java堆可以出于物理上物理上不连续的内存空间中,只要逻辑上连续即可,如同磁盘空间一样,既可以实现成固定大小,也可以是扩展的,当前主流虚拟机都是按照扩展来实现的(通过-Xmx和-Xms控制)。 Java虚拟机规范中对该区域规定了OutOfMemoryError异常:如果堆中没有内存完成实例分配,并且堆无法再扩展则抛出OutOfMemoryError异常。

5 方法区(Method Area)

方法区与Java堆一样,是各个线程共享的内存区域,用于存储一杯虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Java虚拟机对这个区域的限制非常宽松,处理和Java对一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。 Java虚拟机规范中对该区域规定了OutOfMemoryError异常: 如果方法区的内存空间不能满足内存分配请求,那Java虚拟机将抛出一个OutOfMemoryError异常。

注: 运行时常量池(Runtime Constant Pool) 运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面常量和符号引用,这部分内容在类加载后存放到方法区的常量池中。 Java虚拟机规范中对该区域规定了OutOfMemoryError异常: 当常量池无法申请到内存时抛出OutOfMemoryError异常。 直接内存 直接内存并不是虚拟机运行时数据区域的一部分,也非Java虚拟规范中定义的内存区域,但这部分内存也被频繁使用,并且可能导致OutOfMemoryError异常出现。Java虚拟机需要根据实际内存的大小来设置-Xmx等参数信息,如果忽略了直接内存,使得各个内存区域的总和大于物理内存限制,从而导致动态扩展时抛出OutOfMemoryError异常。

一句代码说明Java栈、Java堆和方法区三个最重要的内存区域之间的关联关系 Object obj = new Object();

假设这句代码出现在方法体中:

  • 1)“Object obj”这部分的语义反映到Java栈的本地变量表中,作为一个reference类型数据出现。
  • 2)“new Object()”这个部分的语义反映到Java堆中,形成一块存储了Object类型所有实例数据值的结构化 内存,以及查找到此对象类型的地址信息。
  • 3)Object类的类型数据(如对象类型、父类、接口的实现、方法等)存储在方法区中。
点赞
收藏
评论区
推荐文章
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
1年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Stella981 Stella981
1年前
JVM内存区域划分
JVM内存区域划分一、JVM运行时数据区划分根据《Java虚拟机规范》JVM会把它管理的内存划分为若干个不同的数据区域,如下图所示:方法区、堆、栈(虚拟机栈、本地方法栈)、程序计数器。线程私有的意思是指,JVM每遇到一个新的线程就会为他们分配栈和程序计数器。!(https
Stella981 Stella981
1年前
JVM运行时数据区
Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,及创建和销毁的时间,有些区域随虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束来建立和销毁。Java虚拟机所管理的内存包括以下几个运行时数据区域,如图(图片引自网络):!(https://static.oschina.net/uplo
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
1年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Easter79 Easter79
1年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Stella981 Stella981
1年前
JVM类加载
运行时数据区java虚拟机定义了若干种程序运行时使用到的运行时数据区1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁2.第二种则是与线程一一对应,随线程的开始和结束而创建和销毁。java虚拟机所管理的内存将会包括以下几个运行时数据区域!(http://static.oschina.net/uplo
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_