我是菜鸟:java内存及内存溢出异常

来源:互联网 发布:淘宝联盟已收货不结算 编辑:程序博客网 时间:2024/06/10 09:27

java运行时数据区域

jvm 会将内存区域分成5个区域:程序计数器,java栈,本地方法栈,堆和方法区。其中堆和方法区是所有的线程共享的数据区,另外的3个区是每个线程私有的。

程序计数器

当前线程执行的字节码的行指示器,即指示的是正在执行的 虚拟机字节码的指令地址。当执行的是native方法,此值为空。该区域是唯一一个没有规定任何outOfMemoryError的区域。

java栈

首先java栈是线程私有的,其生命周期和线程是一致的。其用来存放 局部变量表、操作数栈、动态链表和方法出口等信息。在此区域有2中可能的异常:StackOverflowError 和 OutofMemoryError.

本地方法栈

类似于java栈,只不过其为jvm使用的native方法提供服务而已。

java堆

在jvm启动的时候就创建该区域,为所有的线程共享。几乎所有的对象实例和数组都在改区域进行分配。

方法区

其也为所有的线程共享,用于存放被虚拟机加载的类信息,常量,静态变量和即使编译器编译的代码等信息。该区域可以不实现垃圾回收。

以上为jvm运行时的数据部分,即使java jvm规范中定义的内存区域。不过其实在内存中,有一个叫做直接内存的区域也被频繁的使用。

直接内存

在NIO类中,有一种基于通道和缓冲区的IO方式,它可以使用native方法直接在堆外分配内存,然后通过在java堆中的DirectByteBuffer对象作为这块对象的引用来操作这块内存,这块内存叫做直接内存。

Java虚拟机中的对象

对象的创建过程

下面是我的简单理解:
JVM检查类是否被加载,解析和初始化,若没有则
执行相应类的加载,然后
为新生对象分配内存,然后
为分配到的内存空间初始化零值(不包括对象头),然后
jvm对对象进行必要的设置(包括:哪个类的实例,对象的hashcode等,这些值存放在对象头中),然后,
执行init方法(执行new后执行init,将对象按照程序员的意愿进行初始化。)

这样真正的对象才算产生出来。思考:
1. jvm如何对对象分配内存。(2种方式:1. 指针碰撞 2. 空闲列表)
2. 分配内存方式由什么决定?( 直接的是有 Java堆是否规整, 归根结底是由 垃圾收集器是否具有压缩整理功能来决定的。)
3. 在分配内存的时候,要确保线程安全,为什么?如何确保线程安全?(因为在分配内存时候,由于并发,可能前一个对象的指针还未来得及修改,下一个对象就已经开始修改指针,解决方法有2中:1. 同步处理。2 按照线程划分不同的子空间,即java堆中为线程预先分配一小块子空间,叫做本地线程分配缓冲(TLAB)。)

对象的内存布局

对象在内存中存储的布局分为3个区域: 对象头, 实例数据 和对齐填充

对象头

HotSpot对象头包括2部分信息
1. 存储对象自身的运行时数据。(如hashcode, GC分代年龄等。)
2. 类型指针,也就是对象指向它的类元数据指针。通过这个指针确定对象为哪个类的实例。

实例数据

程序中定义的各种类型的字段内容。(所有的字段,包括从父类中继承的字段内容)。
分配策略: 相同的宽度的类型在一起,在此基础上,先存放父类中的字段。

对齐填充

对象的大小为8字节的整数倍,若实例数据不满足,则填充。

对象的访问定位

java程序通过栈中的引用数据来操作堆上的具体对象。对象的访问方式由JVM实现而定。主要为句柄和直接指针2种方式。

句柄访问

栈中的引用指向 对象池中对象的句柄地址 而句柄中包含对象的具体地址。(有点像指针的指针?)

直接指针访问

引用中直接存储对象的地址。

0 0
原创粉丝点击