深入了解JAVA虚拟机读书笔记(一) JAVA内存区域与内存溢出异常

内存划分

在这里插入图片描述
JAVA虚拟机会在执行java程序过程中把其管理的内存划分为若干个不同的数据区域。

为什么方法区和堆是所有线程共享的?

而为什么程序计数器,栈是线程隔离的?

程序计数器

实质:一块小的内存空间
作用:当前线程执行的字节码的行号指示器,决定下一条执行的指令
由于多线程是通过线程轮流切换并且分配处理器执行时间来实现的,任何时刻一个处理器只会执行一条线程的指令,因此为了线程切换后能够恢复到正确的执行位置,每个线程都要有一个独立的PC

如果线程执行JAVA方法: PC记录的是执行指令的地址
执行Native方法: 计数器值为空,此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError的区域

VM stack

与程序计数器一样,Java虚拟机栈也是线程私有的,生命周期与线程相同。

实质:描述java方法执行的内存模型
作用:每个方法执行的时候创建一个栈帧,每一个方法调用到执行完成过程,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

我们常把内存划分为heap与stack,实质上这里的栈就是指虚拟机栈中的局部变量表部分。这个部分所需的内存空间是编译期间完成分配的。

StackOvetflowError

线程请求的栈深度大于虚拟机所允许的深度

OutOfMemoryError

虚拟机可以扩展,如果扩展申请不到足够的内存就会抛出此异常

本地方法栈

与虚拟机栈类似,不过这是为虚拟机所使用到的Native方法服务的。

对了,什么叫本地方法?

简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C

Java堆

  • Java虚拟机中管理内存最大的一块
  • 所有线程共享,虚拟机启动的时候创建
  • 作用是存放对象实例

所有对象实例以及数组都要在堆上分配内存

  • 垃圾收集管理的主要区域
  • 可以处在物理上不连续的内存空间,只要逻辑上是连续的即可

方法区 (Non-heap 非堆)

  • 各个线程共享的内存区域,用于存储被虚拟机加载的各类信息,常量,静态变量,编译器编译后的代码等数据。
运行时常量池
  • 方法区的一部分,用于存放编译期生成的各种字面量与符号引用,类加载完毕后这些内容会存放在运行的常量池中

直接内存

本机直接分配内存不会受到java 堆大小的限制。

既然是内存,肯定还是会受到本机总内存的大小以及处理器寻址空间的限制。

对象访问

Object obj = new Object();

Object obj 这部分将会反映到Java栈的本地变量表中,作为一个reference类型数据出现。

new Object()会反映到Java 堆中,形成一块存储了Object类型所有实例数据值的结构化内存。

Java堆中还要包含能够查到此对象类型数据(对象类型,父类,实现接口,方法等)的地址信息,这些类型数据存储在方法区中。

如何从引用类型访问具体的对象呢?

使用句柄

  • Java堆划分一块内存作为句柄池
  • reference 存储对象的句柄地址
  • 句柄包含了对象实例和类型数据各自的具体地址信息
    在这里插入图片描述

使用直接指针访问

Java堆对象的布局必须考虑如何放置访问类型数据的相关信息。
reference直接存储的就是对象地址

在这里插入图片描述

两种访问方式比较

1.句柄访问:reference中存储的是稳定的句柄地址,对象被移动的时候只会改变句柄中的实例数据指针,reference本身不改变
2.指针访问: 节省了一次指针定位的时间开销,速度更快

实战 OutOfMemory异常

Java堆溢出


import java.util.ArrayList;
import java.util.List;

public class demo{

    static class OOMObject {

    }

    public static void main(String[] args){

        List<OOMObject> list = new ArrayList<>();

        while(true){
            list.add(new OOMObject());
        }

    }
}

虚拟机参数:

-Xss规定了每个线程堆栈的大小。一般情况下256K是足够了。影响了此进程中并发线程数大小。

-Xms初始的Heap的大小。

-Xmx最大Heap的大小。

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
在这里插入图片描述

虚拟机栈或者本地方法栈溢出


import java.util.ArrayList;
import java.util.List;

-Xss256k
public class demo{


    private int stackLength = 1;

    public void stackLeak(){
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args){

        demo oom = new demo();
        try{
            oom.stackLeak();
        }catch (Throwable e){
            System.out.println(oom.stackLength);
            throw e;
        }



    }
}

虚拟机栈容量太小的时候当内存无法分配,跑出栈溢出的异常。


import java.util.ArrayList;
import java.util.List;

public class demo{


   public void donstop(){
       while(true){

       }
   }


   public void stackLeakByThread(){
       while(true){
           Thread thread = new Thread(new Runnable() {
               @Override
               public void run() {
                   donstop();
               }
           });
           thread.start();
       }
   }

   public static void main(String [] args) throws Throwable{
       demo oom = new demo();
       oom.stackLeakByThread();
   }


}

上面这段代码谨慎执行。如果是建立多线程导致的内存溢出,可以通过减少最大堆和减少栈容量来换取更多的线程。

因为给每个线程的栈分配的内存越大,可建立的内存会越小。反而会更容易产生内存溢出异常。

虚拟机栈与本地方法栈内存 = 操作系统内存-最大堆-最大方法区容量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值