java内存区域与内存泄漏异常

一、java内存区域

java内存区域主要包括方法区、堆、虚拟机栈、本地方法栈、程序计数器。

  1. 程序计数器就是记录当前线程执行到哪儿用的,所以一个线程一个程序计数器。
  2. 同样的,虚拟机栈和本地方法栈也是线程私有的。其区别就是虚拟机栈执行的是java方法,本地方法栈执行的是其他语言的方法。虚拟机栈描述的是java方法执行的内存模型。每个方法执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接等。每一个方法从调用到执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。我们平时说的栈就是指虚拟机栈中的局部变量表。局部变量表主要记录了基本数据类型数据、对象引用和returnAddress类型(指向了一条字节码指令的地址。。。不懂)。
  3. 堆主要用于存储对象。
  4. 方法区用于存储类信息、静态变量等。
  5. 运行常量池用于存放常量。jdk1.7以前,常量池在方法区中,jdk1.7及以后,常量池在堆中。
  6. 直接内存,直接内存并不是虚拟机内存区域的一部分,但是经常用。主要通过DirectByteBuffer对象调用。

public class App {
    public static void main(String[] args) {

    }
    
    public int numQuanju = 0;
    
    public void a () {
        final int numJubu1 = 0;
        int numJubu2 = 0;
        String str1 = "str1";
        String str2 = new String("str2");
        Thread t = new Thread(new Runnable() {

            public void run() {
                System.out.println(numJubu1);
                //System.out.println(numJubu2);
                System.out.println(++numQuanju);
            }
            
        });
    } 
}

个人理解:标红的部分应该是在栈中的,而栈是线程私有的,故在t线程中输出numJubu2会报错。但是为什么可以输出numJubu1呢?因为numJubu1被final修饰,它会存放在常量池中,属于共享的,因此不会报错。而栈帧记录的是方法中的局部变量引用,因此numQuanju应该不在栈中,至于是不是在方法区中就不是很清楚了。

 

二、对象的创建

对象创建:

  1. 在常量池中查找类符号
  2. 为新对象分配内存(指针碰撞、空闲列表)
  3. 将内存空间初始化为零(不包括对象头)
  4. 对对象进行必要的设置

 

对象内存分布:

  1. 对象头
  2. 实例数据
  3. 对齐填充

对象头分为两部分:一是存储运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等;二是类型指针,指向方法区中该对象对应的类。

实例数据就是对象的内容了,包括从父类继承过来的,自己定义的。

对齐填充没什么实际意义,就是为了配合实例数据部分达到8字节的整数倍。

 

对象的访问定位:

1、句柄访问

2、直接指针访问(HotSpot使用的就是直接指针访问,也符合上面讲的对象内存分布)

 

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

 

直接指针:如果使用直接指针访问,那么java堆对象的布局中就必须考虑如何防止访问类型数据的相关信息,而reference中存储的直接就是对象地址。

 

这两种对象访问方式各有优势,使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。

 

三、OutOfMemoryError异常

java堆溢出:

Java堆主要有内存溢出和内存泄漏两个问题。内存溢出就是内存不够了,内存泄漏就是存在GC无法回收的内存逐渐累积。个人理解本质上差不多,都可以算是内存溢出。而内存泄漏的情况需要看泄漏的对象是否有保留的需要。

栈溢出:

StackOverflowError、OutOfMemoryError。

系统分配给每个进程的内存是有限制的。而java进程分配到的内存减去堆内存和方法区内存的最大值(程序计数器消耗内存很小,可以忽略不计),剩余的就是堆内存。此时若每个线程分配到的栈容量越大,可以建立的线程数量自然就越小,建立线程时就越容易把剩下的内存耗尽。所以当栈出现OutOfMemoryError时,就要考虑是否可以减少线程数量、是否可以减少堆最大内存或者是否可以减少栈容量。

方法区溢出:

PermGen space

直接内存溢出

由DirectMemroy导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常。如果发现Dump文件很小,而程序中又直接或者间接的使用了NIO,那就可以考虑下是不是这方面的原因。

 

jvm一些常用的参数:

堆最小值    -Xms20m

堆最大值    -Xmx20m

虚拟机内存溢出时Dump出当前的内存转储快照    -XX:+HeapDumpOnOutOfMemoryError

本地方法栈,实际上是无效的,栈容量只由-Xss参数设定    -Xoss   

栈容量        -Xss20m

方法区内存    -XX:PermSize=10M

方法区最大内存    -XX:MaxPermSize=10M

本机直接内存    -XX:MaxDirectMemorySize=10M

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值