Java内存区域与内存溢出

概述

对于从事C,C++程序员来说,程序员负责对内存的管理,拥有每个所创建对象的所有权,担负着每个对象从出生到终结的维护责任。

但是,对于Java程序员来说,Java虚拟机担负起了,对内存管理的责任。如果不了解虚拟机是怎么样使用内存的,那么排查错误将会成为一项异常艰难的工作了

Java内存区域

引用自—https://segmentfault.com/a/1190000006051731
参考图片1
引用自—http://www.cnblogs.com/zhouyuqin/p/5161677.html
详细

  1. 程序计数器(线程私有)
    是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

    字节码解释器工作原理就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器来完成。

    Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说指一个内核)都只会执行一条线程中的指令,为了线程切换后恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。所以这块内存是“线程私有”的内存。

    线程正在执行Java方法,计数器记录正在执行的虚拟机字节码指令的地址 ; 如果执行Native方法,则计数器值为空(Undefined)。

    该区域是虚拟机规范中唯一个没有规定任何OutOfMemoryError情况的区域。

  2. Java 虚拟机栈(线程私有)
    生命周期与线程相同。

    描述的是Java方法执行的内存模型***:每一个方法执行的同时都会创建一个栈帧(Starck Frame)用于存储局部变量表、操作数栈、动态链接、方法出口*等信息。

    每个方法从开始调用到执行完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
    局部变量表中存放了编译期可知的***各种基本数据类型(boolean、byte、char、short、int、float、long、double)以及对象引用(reference类型,它不是对象本身,可能是一个指向:对象起始地址的引用指针、对象句柄)和returnAddress类型(指向一条字节码指令的地址)。***
    虚拟机规范中对这块区域规定了两种异常状况:
    1> 如果线程请求的栈深度大于虚拟机所允许的深度将抛出StackOverflowError异常
    2> 如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常

  3. 本地方法栈(线程私有)
    与虚拟机栈的作用非常相似,区别就是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈为虚拟机使用到的Native方法服务。SunHotSpot虚拟机把虚拟机栈和本地方法栈合并了。

    与虚拟机栈一样本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常

  4. Java堆(虚拟机启动时创建,线程共享)
    大多数应用中,堆被认为是虚拟机内存中最大的一块。所有对象实例以及数组都分配在堆上, 但是随着Jit技术和逃逸分析,栈上分配,标量替换等技术的发展,界限也变得不是那么绝对。

    堆是垃圾收集器管理的主要区域(也叫作GC堆)。从内存回收的角度看,收集器基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代,再细致一点:Eden空间、From Survivor空间、To Survivor空间。从内存分配角度来看,线程共享的Java堆中,还可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。无论哪个角度都是为了更好更快的回收和分配内存。

    Java堆的实现可以使固定的大小,也可以是可扩展的,主流一般是通过可扩展的方式实现(通过-Xmx和-Xms控制)

    如果Heap中没有足够的内存完成实例的分配,并且也无法再扩展时将会抛出OutOfMemoryError异常

  5. 方法区(线程共享 Non-Heap(非堆))
    对于HotSpot虚拟机上开发者也把这块区域成为“永久代”(Permanent Generation)。两者本质上并不等价,只是为了垃圾收集器可以像管理 Java 堆一样去管理这部分区域而把GC分代收集扩展到了这里。

    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译(Just In Time,JIT编译器)后的代码等数据(JDK 1.7 中字符串常量池已被移出)。
    可以通过-XX:PermSize 和 -XX:MaxPermSize来设置这块区域的可扩展性
    当方法区不能满足内存分配需求时,会抛出OutOfMemoryErrory

  6. 运行时常量池(属于方法区)
    用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后存入方法区的运行时常量池中(动态性)。

    当常量池无法申请到内存时会抛出OutOfMemoryErrory。

  7. 直接内存(既不属于虚拟机运行时内数据区,也不是Java虚拟机规范中定义的内存区域,使用较频繁)

    服务器管理员在配置虚拟机参数时(如-Xmx等参数),如果忽略了直接内存,使各个内存区域总和大于物理内存限制(包括物理的和操作系统的限制),从而导致动态扩展时出现OutOfMemoryError异常。

总结

堆内存主要存放实体对象,用来存放new的对象和数组引用变量的实体对象。由虚拟机自动垃圾回收器管理。
栈内存主要存放基本基本类型的变量。
方法区主要存放一个类的模板和静态方法。
声明位置的不同决定储存位置

在方法中声明(局部变量):调用方式时在栈中分配空间给变量,结束时释放栈,体现变量的局限性。
在类中声明(成员变量):由于作用周期需要,变量存放在堆中。不会因方法销毁为失效,类似于C中的全局变量。
对于引用变量来说,对应内存所储存的值是一个引用,是对象的储存地址,被存放在栈中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值