java的内存区域

内存系列相关文章

java的内存区域

垃圾回收器及内存分配策略

垃圾回收器

内存分配与回收策略

  • 概述
  • 区域划分
  • 对象访问
  1. 概述

    对于C或c++的程序员来说,他们对内存管理拥有至高无上的权力。既是拥有最高权力的皇帝,又是从事基础工作的劳动人民。

    而对于java程序原来说,内存的管理使在内存管理机制的帮助下完成的,new出来的对象不需要手动去释放,节省了一定的人力成本。这样的内存处理机制看上去使那么的美好,但一旦出现内存的泄露和溢出,如果对虚拟机的内存处理机制不是很了解,排查错误将是一件很困能的事。这篇就是介绍虚拟机的内存管理问题。

  2. 区域划分

Java虚拟机把他所管理的内存划分为若干个不同的数据区域,根据《Java虚拟机规范》的规定,java虚拟机所管理的内存将会包含以下几个运行时数据区域。

 

  • 程序计数器

程序计数器是一块较小的内存空间,他的作用是dang当前线程所执行的字节码的行号治时期,字节码计时器工作时是通过改变这个计数器的值来选取下一条xu'y需要执行的字节码zhi'指令。

java虚拟机的duo'多线程是通过xian线程轮流切换bing并分配处理器执行时间的方式实现的,在任何一个特定的时刻,处理器zhi'只会执行一条线程中的指令,为了能切换后恢复到正确的执行位置,每个线程需要一个独立的程序计数器,因此程序计数器是线程私有的。

  • java虚拟机栈

java虚拟机栈就是我们常说的堆栈中的栈,和程序计数器一样也是线程私有的,生命周期与线程相同,java在每个方法执行时都会创建一个栈帧,用于存储局部变量表,操作栈,动态链接,方法出口等信息。每个方法的执行过程都对应着一个栈帧从入栈到出栈的过程。因此在方法中申明的变量的生命周期是短暂的,随着方法的结束而释放,因此在频繁的调用一个方法时不能在里面创建一些比较大的对象,即使创建也要复用,不然会造成频繁的gc,造成资源的浪费,内存抖动,影响性能,严重时造成oom

局部变量表所需要的内存空间是在编译期间完成分配的,dang当进入一个方法时,这个方法所需要的局部变量空间是确定的,在方法运行时是不会改变这个局部变量表的大小的。

在java虚拟机规范中,对这个区域规定了两种异常:如果线程请求的栈深度大于虚拟机允许的深度,将会抛出StackOverflowError异常(在方法嵌套调用时,尤其是递归方法中必须注意);当虚拟机栈dong动态扩展时,如果无法申请到足够的内存时就会抛出OutOfMemoryError(平时注意对象的合理创建)。

  • 本地方法栈

本地方法栈与虚拟机栈类似,区别不过是虚拟机栈为虚拟机执行java方法服务,而本地方法栈为Native方法服务,与虚拟机栈一样也会抛出StackOverflowError和OutOfMemoryErroryi'c异常。

  • java堆

对于大多数应用来说,java堆是虚拟机所管理的内存中最大的一块一块,被所有的线程共享,在虚拟机启动时创建,可动态扩展。在这块区域中存放的是对象的实例,几乎所有的对象都在这里分配内存。

java堆是垃圾回收器管理的主要区域,因此很多时候也被称为GC堆。由于现在收集器都是采用的分代收集算法,所以java堆又可以细分为:新生代和老年代。根据java虚拟机规范的要求,Java堆可以是物理上不连续的内存空间,只要求逻辑上连续即可,它是可扩展的,当堆中没有内存完成实例分配,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。

  • 方法区

方法区与Java堆一样,是各个线程共享的内存区域,用于存储被虚拟机加载的类的信息,常量,静态变量,即时编译器编编译器编译后的代码等数据。根据java虚拟机规范,当方法区无法满足内存分配需求时,将会抛出OutOfMemoryError异常。

  • 直接内存

直接内存不是Java虚拟机规范中定义的内存区域,但这部分内存也是频繁使用,在JDK1.4中新加入了NIO类,引入了一种基于通道与缓冲区的I/O方式,他可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆里的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,避免了在java堆和native堆中来回复制数据。

  3.对象的访问

对象的访问在java语言中无处不在,即使最简单的访问也要涉及到java栈,Java堆,方法区这三个最重要的内存区域。如下代码:Object obj = new Object();

Object obj 这部分将反映到Java栈中的本地变量表中,作为reference类型出现。new Object这部分将反映到java堆中,形成一块存储了Object类型所有实例数据值的结构化内存。另外在java堆中还必须包含能查到此对象的数据类型(如对象类型,父类,实现的接口,方法等)的地址信息,这些数据类型数据则存储在方法区中。

reference是指向对象的引用,具体的定位方式主要有两种:使用句柄和直接指针

          如果使用句柄的方式,java堆中将会划分出一块内存出来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

          如果使用直接指针的访问方法,Java堆对象的布局中就必须考虑如何放至访问类型数据相关的信息,reference中存储的就是对象的地址。

这两种对象访问方式各有优势,使用ju'b句柄访问最大的好处是reference中存储的是稳定的句柄地址,在对象被移动(在GC时对象移动是非常普遍的)时只会改变句柄中实例的指针,而reference本身不会被修改;使用直接访问的方式最大的好处是速度快,它节省了一次指针定位的时间开销。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值