java内存区域与内存溢出异常

一、运行时数据区域  

                                 

其中:堆和方法区由所有线程共享,栈和程序计数器是线程私有。

程序计数器 :

         程序计数器可以看做是当前线程所执行的字节码的行号指示器,虚拟机的概念模型中就是通过改变计数器的值来选择下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复到等等基础功能都需要依赖计数器来完成。

         如果线程正在执行的是java方法,程序计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,则计数器为空。

         程序计数器是jvm中唯一一个不会出现OOM的内存区域。

java虚拟机栈:

        虚拟机栈是线程私有的,它的生命周期和线程相同。

        每个方法在执行的同时都会创建一个栈帧用来存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行结束的过程,就对应着一个栈帧在虚拟机中入栈和出栈的过程。

       局部变量表存放了编译期可知的各种基本数据类型(boolean , byte , char , short , int , float , long , double)、对象引用类型和retreturnAddress类型(指向一条字节码指令的地址)。其中64位长度的double和 long 类型的数据会占用2个局部变量表空间,其余数据类型只占用一个。局部变量表所需的空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量表空间是确定的,方法运行期间不会改变局部变量表的大小。

      虚拟机栈可能会出现两种异常 :

  1. 如果线程请求的栈深度大于虚拟机所允许的栈深度,就会抛出StackOverflowError异常。
  2. 虚拟机进行动态扩展时无法申请到足够的内存,就会抛出OutofMemoryError异常。

本地方法栈:

     本地方法栈和虚拟机栈类似,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈(JNI)则为虚拟机使用到的Native方法服务。与虚拟机栈一样,本地方法栈也会抛出SrackOverflowError异常和OutofMemoryErroe异常。

Java堆:

       对于大多数应用来说,Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,几乎所有的对象实例都在堆中分配内存。

      Java堆是垃圾收集器管理的主要区域(“GC堆”),从内存回收的角度看,由于现在收集器基本都采用分代收集算法吗,所以Java堆中还可以细分为 : 新生代和老年代。

      java堆可以处于物理上不连续的内存空间,在实现上,既可以实现成固定大小的,也可以是可扩展的(通过-xmx 和 - xms 设置最大堆空间和初试堆空间)。如果堆进行动态扩展无法申请到足够内存的话就会抛出OOM异常。

方法区:

       方法区是线程共享的数据区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译的代码等数据,

      和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。

      对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。

      HotSpot 虚拟机把方法区当成永久代来进行垃圾回收。但很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。

        方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中

        jdk1.7之前:方法区位于永久代(PermGen),永久代和堆相互隔离,永久代的大小在启动JVM时可以设置一个固定值,不可变;
        jdk.7:存储在永久代的部分数据就已经转移到Java Heap或者Native memory。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native memory;字符串常量池(interned strings)转移到了Java heap;类的静态变量(class statics variables )转移到了Java heap;
        jdk1.8:仍然保留方法区的概念,只不过实现方式不同。取消永久代,方法存放于元空间(Metaspace),元空间仍然与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中。

  • 1)移除了永久代(PermGen),替换为元空间(Metaspace);
  • 2)永久代中的 class metadata 转移到了 native memory(本地内存,而不是虚拟机);
  • 3)永久代中的 interned Strings 和 class static variables 转移到了 Java heap;
  • 4)永久代参数 (PermSize MaxPermSize) -> 元空间参数(MetaspaceSize MaxMetaspaceSize)。

 运行时常量池:

       运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行是常量池中。

       运行时常量池具动态扩展性,java不要求常量一定只有编译期才能产生,运行期间也可以将新的常量放入池中,这种特性被利用的比较多的就是String类的intern()方法。当常量池动态扩展时无法申请到足够内存时会抛出OOM异常。

直接内存:

       JDK1.4 新加入了NIO类,引入了一种基于通道(Channel)和缓存区(Buffer)的I/O方式,他可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景提高性能,避免了在Java堆和Native堆中来回复制数据。 动态扩展时也会出现OOM异常。

 

二、HotSpot虚拟机对象探秘 

对象的创建 :

        虚拟机遇到一条new指令时,首先先检查这个指令的参数是否在能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过。如果没有,就必须先执行相应的类加载过程。

        类加载检查通过后,虚拟机将会为新生对象分配内存,对象所需内存的大小在类加载完成后就可以完全确定。

        内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,保证了对象的实例字段在java代码中可以不赋值初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。

        接下来,虚拟机会对对象进行必要的设置,例如这个对象是属于哪个类的实例,如何才能找到类的元数据信息,对象的hash码,对象的GC分代年龄等,这些信息存放在对象的对象头中。

       接下来会执行init()方法,把对象按照程序员的意愿进行初始化(赋值初始值等),这样一个真正可用的对象才算产生出来。                  

对象的内存布局:

        在HotSpot虚拟机中,对象在内存的存储的布局可以分为3块区域,对象头(Header),实例数据(Istance Data)和对称填充(Padding)。

       HotSpot虚拟机的对象头包括了两部分信息,第一部分用于存储对象自身的运行时数据,如Hash码,GC分代年龄,锁状态标志,线程持有的锁,偏性锁ID,偏向时间戳等,对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

       接下来的实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类定义的,都需要记录下来。

      第三部分对齐填充不是必须的,仅仅起着占位符的作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值