zoukankan      html  css  js  c++  java
  • java内存区域与内存溢出异常

    运行时数据区域

    一、程序计数器

    程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。

    虚拟机多线程中,为了线程来回切换可以找到各自的位置,各自都需要计数器。

    如正在执行的是Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址

    如正在执行的是Native方法,计数器记录为空

    此内存区域是唯一一个没有OutOfMemoryError

    二、java虚拟机栈

    描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至自行完成的过程,就对应着一个栈帧在虚拟机中的入栈与出栈的过程。

    局部变量表中存放了编译期可知的各种基本数据类型、对象引用(不等于对象本身)等。

    局部变量表所需的内存空间在编译期间完成分配,运行时不会改变大小。

    存在两个异常:

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

    OutOfMemoryError:如果虚拟机栈可以动态扩展(大部分都可以扩展),扩展时无法申请到足够的内存。

    三、本地方法栈

     虚拟机栈为虚拟机执行java方法服务;本地方法栈为虚拟机使用到的Native方法服务。

    存在两个异常:

    StackOverflowError OutOfMemoryError

    四、java堆

    最大的一块区域

    唯一的目的存放对象实例

    垃圾回收器管理的主要区域

    可以处于物理上不连续的内存空间,逻辑上连续即可

    可扩展通过(-Xmx和-Xms控制)

    堆无法扩展抛出OutOfMemoryError

    五、方法区

    用于存储已被虚拟机加载的类信息、常量、静态变量等数据

    堆的一个逻辑部分

    很多人称为“永代区”,但不准确,字符串常量池已经移除

    回收的效率不高但是有必要进行

    方法区无法满足内存分配时,抛出OutOfMemoryError

    六、运行时常量池

    Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放前一期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

    注:直接内存(并不是运行时数据区)

    基于通道与缓冲区(NIO)此处需要扩充看

    避免了在java堆和Native堆中来回复制数据

    HotSpot虚拟机对象探秘

    一、对象的创建

    1.虚拟机遇到一条new指令,首先检查常量池能否找到类的引用符号,并检查这个引用代表的类是否已被加载、解析和初始化。如果没有先加载类

    2.加载后为新生对象分配内存,大小在加载后即确定

    (1)分配有两种方法

    “指针碰撞”(java堆中内存绝对规整,用过的放一边,空闲的放一边,中间放指针,要用就挪动指针)

    “空间列表”(不规整,虚拟机就维护一个列表)

    是否规整由采用的垃圾收集器是否压缩整理功能决定

    (2)对象创建在虚拟机中是非常频繁地行为,并发情况下并不安全

    两种方案:

    对分配动作同步处理

    把内存分配工作按线程划分在不同的空间进行,即每个线程预先分配一小块内存,成为本地线程分配缓冲(TLAB),哪个线程要分配,就在TLAB上分配

    3.内存分配后虚拟机要将分配到的内存空间都初始化为零值,(保证了)java代码中不赋初始值就可以使用

    4.虚拟机要对对象进行设置,如对象是哪个类的实例,对象的哈希码等,这些信息都在对象头中

    5.从虚拟机的视角,一个新的对象已经产生了,但从java程序,<init>还没执行,所有字段都为0,所以执行new之后,接着执行<init>,进行初始化,这样对象才算产生

    二、对象的内存布局

    3块区域:对象头、实例数据(真正的有效信息)、对齐填充(占位作用)

    三、对象的访问定位

    两种方式:

    使用句柄:java堆划分出块内存作为句柄池,reference(操作对象)中存储的就是句柄地址

    直接指针:直接地址

    优点:

    句柄:稳定

    指针:速度快

    OutOfMemoryError异常

    一、java堆溢出

    对象数量达到堆的容量限制

    不可扩展(将堆的最小值-Xms参数与值-Xmx参数设置为一样即可避免堆自动扩充)

    通过(-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时,通过dump进行分析)

    解决办法:通过内存影响分析工具对Dump出来的堆转储快照进行分析,分析出是内存泄漏还是内存溢出。

    如果是内存泄漏(程序申请内存后,无法正常释放),查看泄露对象到GC Roots的引用链,就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾回收器无法自动回收他们的。

    如果是内存溢出(申请内存时没有足够的空间),不存在泄露,就是内存中的对象还必须存活着,那就检查虚拟机的堆参数(-Xmx与-Xms),与及其物理内存对比看是否还可以调大。

    二、虚拟机栈和本地方法栈溢出

    -Xss设置栈大小

    为每个线程的栈分配的内存越大,反而越容易产生内存溢出,因为每个线程分配到的栈容量越大,可以建立的线程数量就越少,建立线程时就越容易把剩下的内存耗尽。

    如果建立线程导致内存溢出,在不能减少线程数或更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。

    三、方法区和运行时常量池溢出

    JDK1.6以前运行时常量池是在永代区

    String.intern()是一个Native方法:作用:如果字符串常量池中已经包含了一个等于此String队形的字符串,则返回代表池中这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池,并且返回此String对象的引用。

    String str1 = new StringBuilder("计算机”).append("软件").toString();

     System.out.println(str1.intern() == str1);

    String str2 = new StringBuilder("ja”).append("va").toString();

     System.out.println(str2.intern() == str2);

     JDK1.6是两个false:,JDK1.7是一个true一个false:intern()会把首次遇到的字符串实例复制到常量池,返回的是常量池中的引用,而StringBuilder的实例是在java堆上,。而JDK1.7的intern()不会复制实例,知识在常量池中记录首次出现的实例引用。

    方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。这一区域的测试基本的思路就是填满直到溢出,比如CGLib对类的动态增强还有JVM的动态语言Groovy。

    四、本机直接内存溢出

    DirectMemory容量可通过-XX:MaxDirectMemorySize指定,不指定则与java堆最大值(-Xmx指定)一样

    有DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中看不到明显的异常。 

  • 相关阅读:
    JavaScript中的闭包
    SQL 备忘
    SqlServer 2005 升级至SP2过程中出现"身份验证"无法通过的问题
    unable to start debugging on the web server iis does not list an application that matches the launched url
    Freebsd 编译内核
    Freebsd 6.2中关于无线网络的设定
    【Oracle】ORA01219
    【Linux】Windows到Linux的文件复制
    【Web】jar命令行生成jar包
    【Linux】CIFS挂载Windows共享
  • 原文地址:https://www.cnblogs.com/jqqiang/p/7684272.html
Copyright © 2011-2022 走看看