资料整理自网络(侵删)

JVM内存

组成
JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)

  • 栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
 
  • 堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
 
  • 方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。
        Java中通过多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存区域main memory,而每个线程又单独的有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数)
 
JVM的逻辑内存模型:
  • 程序计数器
        程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
  • Java 虚拟机栈
        存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用和returnAddress 类型(指向了一条字节码指令的地址)
        如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;如果虚拟机栈可以动态扩展
 (当前大部分的Java 虚拟机都可动态扩展,只不过Java 虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError 异常。
  • 本地方法栈
        本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。本地方法栈区域也会抛出StackOverflowError 和OutOfMemoryError异常。
  • Java 堆
        所有的对象实例以及数组都要在堆上分配(但是随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换②优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。)
        Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC 堆”(Garbage Collected Heap)。Java 堆中还可以细分为:新生代和老年代;再细致一点的有Eden 空间、From Survivor 空间、To Survivor 空间等。
  • 方法区
       HotSpot 虚拟机中的 “永久代”(Permanent Generation),除了和Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集,相对而言,垃圾收集行为在这个区域是比较少出现的,。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载
        运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
 

加载运行机制

创建对象
Object obj = new Object();
  • “Object obj”这部分的语义将会反映到Java 栈的本地变量表中,作为一个reference 类型数据出现
  • “new Object()”这部分的语义将会反映到Java 堆中,形成一块存储了Object 类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。另外,在Java 堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。
访问:
  • 使用句柄访问:
  • 直接指针访问:
 
局部变量
  局部变量是指在方法中声明的变量:当变量为基本类型时,存储的是变量的值;当变量为引用类型时,存储的是变量指向所指向地址的值。
  • 局部变量的生命周期
Java程序从开始到结束会有很多次的方法调用,在方法调用时,JVM会为每一个方法在栈中分配一个空间:栈帧,一个栈帧对应一个正在调用的方法,用于存放方法中的局部变量和参数等数据,当方法调用结束时,JVM会吧栈帧清除出去。
  • 成员变量和局部变量的差别

局部变量:-定义在方法中    -没有默认值,必须设定初始值    -方法调用是,存在栈中,方法调用结束,从栈中清除

成员变量:-定义在类中    -有默认值,可以不显示初始化    -所有类实例化,存在堆中,对象被收回时,成员变量失效
 
类的加载
        方法区用于存放类的信息,Java程序运行时,首先通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。
        方法只有一份:当类的信息装载到方法区中,除了类的信息外,同时类的方法定义也被加载到方法区。类在实例化对象时,多个对象在堆中有各自的空间,但所有的实例化对象时公用在方法区中的一份方法定义。
 

初始化机制

类初始化
  1. 什么时候类加载:第一次需要使用类信息时加载。(虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化)
  2. 类加载的原则:延迟加载,能不加载就不加载。

  • 静态内部类字段初始化
-JAVA中静态内部类字段什么时候初始化?是在外部类加载的时候就初始化吗?(单例模式登记式/静态内部类实现的原理)
-不是的,只有在加载内部类的时候才初始化   
 
登记式/静态内部类(lazy初始化、多线程安全)(利用类创建实例自动处理多线程安全;Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有显示通过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instance)
publicclassSingleton{ 
    privatestaticclassSingletonHolder{ 
    privatestatic final Singleton INSTANCE =newSingleton(); 
    } 
    privateSingleton(){} 
    publicstatic final Singleton getInstance(){ 
    returnSingletonHolder.INSTANCE; 
    } 
}   

  • 类的加载过程  
顺序是:父类静态属性-》父类静态代码块-》子类静态变量-》子类静态代码块-》父类非静态变量-》父类非静态代码块-》父类构造函数-》子类非静态变量-》子类非静态代码块-》-》子类构造函数 (这样的加载顺序不是绝对的 因为静态变量和静态代码块跟声明顺序有关)
有父类的情况:
1. 加载父类
    1.1 为静态属性分配存储空间并赋初始值
    1.2 执行静态初始化块和静态初始化语句(从上至下)
2. 加载子类
    2.1 为静态属性分配存储空间
    2.2 执行静态初始化块和静态初始化语句(从上至下)
3. 加载父类构造器
    3.1 为实例属性分配存数空间并赋初始值
    3.2 执行实例初始化块和实例初始化语句
    3.3 执行构造器内容
4. 加载子类构造器
    4.1 为实例属性分配存数空间并赋初始值
    4.2 执行实例初始化块和实例初始化语句
    4.3 执行构造器内容
5  回到main()
内部类的加载过程也一样
 
对象初始化
《疯狂Java程序员的基本修养》

JAVA垃圾回收机制

垃圾回收机制
Java中那些不可达的对象就会变成垃圾
算法:引用计数法、停止-复制(stop and copy)、标记-清扫(mark and sweep)
分代回收:
内存泄漏
java heap的内存泄漏:
        如果java heap容量扩充到上限,并且在GC后仍然没有足够的空间分配新的java对象,便会抛出out of memory异常,导致JVM进程崩溃
编程技巧
        根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。一些关于程序设计的几点建议:
  • 1.最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null.我们在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null.这样可以加速GC的工作。
  • 2.尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。
  • 3.如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory.
  • 4.注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。
  • 5.当程序有一定的等待时间,程序员可以手动将你认为不需要的数据设为null,然后执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。(可能会影响效率)
 

JAVA虚拟机与内存的更多相关文章

  1. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  2. 深入理解java虚拟机【内存溢出实例】

    通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收 ...

  3. 从Java虚拟机的内存区域、垃圾收集器及内存分配原则谈Java的内存回收机制

    一.引言: 在Java中我们只需要轻轻地new一下,就可以为实例化一个类,并分配对应的内存空间,而后似乎我们也可以不用去管它,Java自带垃圾回收器,到了对象死亡的时候垃圾回收器就会将死亡对象的内存回 ...

  4. Java虚拟机:内存模型详解

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实 ...

  5. 初识:java虚拟机的内存划分

    什么是内存? 内存是计算机中的重要原件,临时存储区域,作用是运行程序.我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存.Java虚拟机要运行程序 ...

  6. java虚拟机的内存模型

    一.为什么要了解java虚拟机的内存模型 java虚拟机作为java代码运行的平台,是java技术的基石.了解java虚拟机的内存模型也就变得十分必要.它能帮助我们更好的了解java代码的运行机制,更 ...

  7. java虚拟机的内存机制

    我们都知道,java程序的跨平台性离不开java虚拟机,虚拟机隔绝了底层操作系统,使得java程序可以直接运行在虚拟机之上.所以,对java的学习,离不开对java虚拟机的学习与了解.下面简单整理下j ...

  8. Java虚拟机的内存管理

    众所周知,Java程序员写的代码是没有办法控制Java对象的内存释放的,完全有JVM暗箱操作. 虽然程序员把内存的释放的任务都交给了Java虚拟机,但是并不代表Java程序就不存在内存泄漏. 反而,某 ...

  9. 1 - JVM随笔分类(java虚拟机的内存区域分配(一个不断记录和推翻以及再记录的一个过程))

    java虚拟机的内存区域分配   在JVM运行时,类加载器ClassLoader在加载到类的字节码后,交由jvm的执行引擎处理, 执行过程中需要空间来存储数据(类似于Cpu及主存),此时的这段空间的分 ...

  10. JAVA虚拟机:内存各个区介绍

    概述:java应用程序由java虚拟机自动管理程序执行期间内存管理. 优势:1.不再需要程序员去为使用的内存在程序中手动编写释放内存代码. 2.由虚拟机管理内存不容易出现内存泄漏和内存溢出的问题. 缺 ...

随机推荐

  1. 设置Imindmap默认字体

    创建一个新的字体样式 根据如下步骤创建新的字体样式: 1.打开一个mindmap,选中工具栏上的 [样式][Styles ]. 2.选择 Font > Create New Font Optio ...

  2. 一种实现C++反射功能的想法(一)

    Java的反射机制很酷, 只需知道类的名字就能够加载调用. 这个功能很实用, 想象一下, 用户只需指定类的名称, 就可以动态绑定类型, 而且只需通过字符串指定, 字符串的使用可以使得用户的修改只需修改 ...

  3. App Store自动下载WiFi与蜂窝数据切换机制

    写下这个给自己备忘,上次也有一次载了个跟头. 在iOS 7和8里面,除了设置--App Store里面自动更新,自动下载,以及使用蜂窝数据要关之外,别以为用了WiFi挂着程序,就万无一失了. 这种情况 ...

  4. nodejs读取本地txt文件并输出到浏览器

    var fs = require('fs'); var chrome=""; //同步执行 function tongbu(){ var data =fs.readFileSync ...

  5. 如何让IIS 8.0支持无后缀图片的访问

    进入“MIME类型”模块后,我们点击右侧的“添加”,然后填好文件扩展名和类型值.对于无后缀的图片文件,扩展名只需填写“点”符号即可,类型值根据图片文件实际的扩展名填写.如果是jpeg格式的,那么就填写 ...

  6. python中如何用sys.excepthook来对全局异常进行捕获、显示及输出到error日志中

    使用sys.excepthook函数进行全局异常的获取. 1. 使用MessageDialog实现异常显示: 2. 使用logger把捕获的异常信息输出到日志中: 步骤:定义异常处理函数, 并使用该函 ...

  7. SPSS时间序列分析

    时间序列分析必须建立在预处理的基础上…… 今天看了一条新闻体会到了网络日志的重要性…… 指数平滑法(Exponential Smoothing,ES)是布朗(Robert G..Brown)所提出,布 ...

  8. mongodb3.0 db.addUser报错

    > db.addUser("ydkt","ydkt") --19T09:: E QUERY TypeError: Property 'addUser' o ...

  9. java正则表达式一:基本使用

    java正则表达式主要涉及三个类:java.util.regex.Matcher.java.util.regex.Pattern.java.util.regex.PatternSyntaxExcept ...

  10. 调用系统API还是很高效的,不必担心性能

    代码如下: void MainWindow::on_pushButton_2_clicked() { QTime total; total.start(); ; ; i<=*; i++) { Q ...