zoukankan      html  css  js  c++  java
  • 深入理解JVM-java内存区域与内存溢出异常

    1.内存模型概述

      

    2.运行时数据区

    2.1.程序计数器

      理解:

      1.什么是程序计数器

      2.线程私有还是共享

      引入难点:

      理解什么是 native方法

     简单地讲,一个Native Method就是一个java调用非java代码的接口。
    一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。
    这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。 "A native method is a Java method whose implementation is provided by non-java code." 在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。,下面给了一个示例: public class IHaveNatives { native public void Native1( int x ) ; native static public long Native2() ; native synchronized private float Native3( Object o ) ; native void Native4( int[] ary ) throws Exception ; }

    2.2.java虚拟机栈

      https://www.cnblogs.com/newAndHui/p/11168791.html

    2.3.本地方法栈

      本地方法栈(Native Method Stack) 与虚拟机栈所发挥的作用是相似的,

      他们之间的区别是:

      1.虚拟机栈为虚拟机执行java方法(也就是字节码)服务;

      2.本地方法栈则为虚拟机使用的Native方法服务.

      与虚拟机方法一样,本地方法栈区域也会就抛出StackOverflowError 和  OutOfMemoryErrory异常.

    2.4.java堆

      

      1. Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,是虚拟机所管理的内存中最大的一块。

    此内存区域的唯一目的就是【存放对象实例和数组】,几乎所有的对象实例和数组都在这里分配内存。

      2. Java堆是垃圾收集器管理的主要区域,也称为GC 垃圾堆。后面会专门讲解GC算法。

      从内存回收的角度看,由于现在收集器基本都采用分代收集算法,所以Java堆可以细分为:新生代、老生代

      从内存分配的角度看,线程共享的Java堆可能划分出多个线程私有的分配缓冲区(TLAB);

      不论如何划分,都与存放的内容无关,无论哪个区域,存储的仍然是对象实例和数组

      3. 如果在堆中没有内存完成实例分配,并且堆上也无法再扩展时,将会抛出OutOfMemoryError异常。

      4. 内存泄露和内存溢出

      内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;

      内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

      内存泄漏的累积最终会导致内存溢出!

      案例演示:

      参看:https://www.cnblogs.com/newAndHui/p/11105956.html 的 2.4节 内存快照分析

    2.5.方法区

      

      1. 和堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

      2. Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样 不需要连续的内存和可以选择固定大小或者可扩展之外,还可以选择不实现垃圾回收

      这区域的内存回收目标主要是针对常量池的回收类型的卸载

      一般而言,这个区域的内存回收比较难以令人满意,尤其是类型的回收,条件相当苛刻,但是这部分区域的内存回收确实是必要的。

      3. 很多开发者更愿意把方法区称为“永久代”(Perm Gen)(Permanent Generation)「总是存放不会轻易改变的内容」,

      原因:HotSpot的方法区也是采用的GC分代垃圾收集,省去了专门为方法区编写内存管理的工作。

      在目前已经发布的JDK 1.7 的HotSpot中,已经把原本放在永久代(方法区)的字符串常量池移至堆中

      4. 运行时常量池(Runtime Constant Pool)是方法区的一部分(已移走)。

      5.JDK 1.8 中,已经没有方法区(永久代),而是将方法区直接放在一个与堆不相连的本地内存区域(Native Memory),这个区域被叫做元空间

      6.当无法满足内存分配需求时:将会抛出OutOfMemoryError异常

    2.6运行时常量池

      

      1. 运行时常量池是方法区的一个部分

      2. Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的字面量符号引用

      这部分内容(也可以称为 .Class文件中的静态常量池)将在类加载后进入方法区的运行时常量池中存放。

      字面量 : 比较接近Java语言层面的常量概念,如文本字符串、声明为final的常量值等。(final修饰的成员变量和类变量!「类变量即静态(成员)变量)」,也就是除final修饰的局部变量。
    
      符号引用 : 属于编译原理方面的概念,包括 
        1.类和接口的全限定名(即路径,包名+类名)。 
        2.字段的名称和描述符。 
        3.方法的名称和描述符。 
      当虚拟机运行时,需要从常量池获得对应的符号引号,再在类创建或运行时解析、翻译到具体的内存地址之中(直接引用)。

      3. 除了保存Class文件中描述的符号引用外,还会把编译出来的直接引用也存储在运行时常量池中。

      4. Java语言并不要求常量一定只有编译期才能生成,也就是并非置入Class文件中常量池的内容才能进入方法区运行时常量池,

      运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String 类的intern()方法。

      5. 当常量池无法再申请到内存时也会抛出OutofMemoryError异常。

    2.7直接内存

      

      1.并不是虚拟机运行时数据区的第一部分
      2.也不是java虚拟机规范中定义的内存区域
      3.直接内存分配不会受到java堆大小的限制,但受到本机总内存限制
      4.用处:直接使用Native函数分配该内存
      5.抛出OutofMemoryError异常

    2.8扩展

      成员变量与局部变量

      成员变量 : 方法外部,类内部定义的变量;

      局部变量 : 方法或语句块内部定义的变量,必须初始化

    1. 形参是局部变量,实参则可能是方法中的局部变量或全局变量。
    2. 栈内存中的局部变量随方法而生,随方法而灭。
    3. 成员变量存储在堆中的对象里,由垃圾收集器回收。

       

    参考文献:

    <<深入理解Java虚拟机:JVM高级特性与最佳实践>> 

  • 相关阅读:
    .NET旋转PDF并保存旋转结果到文件
    C#的抽象类和接口的区别,在什么时候使用才合适?
    [转]SQL Server中多行多列连接成为单行单列
    VBS脚本COPY指定日期文件及文件夹
    eval同时绑定两个值:通过String.Format给超链接中的两个参数赋值
    How to recover SA password on Microsoft SQL Server 2008 R2
    [转]asp.net 前台绑定后台变量方法总结:<%= %> 和<%# %>的区别
    Js得到radiobuttonlist选中值的两种方式
    Ameriscan增加一个新的client:AP_CONSO
    命令行处理pdf的利器:PDFTK.exe
  • 原文地址:https://www.cnblogs.com/newAndHui/p/11107582.html
Copyright © 2011-2022 走看看