Java虚拟机

一、虚拟机内存分区

java虚拟机运行在受不同操作系统操纵的物理机上,不同的操作系统使用不同的底层方法来执行不同的操作,这些方法称之为本地方法:Native Method,本地方法一般执行的都是比较底层的操作,比如说IO、线程管理等,java方法则会执行的一般是相对高级的操作,比如说数逻运算,或者是调用底层的本地方法来完成底层任务。java虚拟机的运行时数据区域将内存分成了不同的部分协调完成java虚拟机的内存数据交互。

按照数据存储过程的数据结构可以大致分为:

栈区:

  • 虚拟机栈:java虚拟机运行的java方法(java字节码方法)构成的栈空间,这个空间在运行时存储这些方法的局部变量表、操作栈、动态链接和方法出口;
  • 本地方法栈:本地方法在运行时存储数据产生的栈区。

堆区:

  • java堆:对象的实例存储在这个共享的堆空间里,由于占有最大的和最有实际意义的空间,这个空间的GC过程时虚拟机运行的重点。
  • 方法区:存储虚拟机运行时加载的类信息、常量、静态变量和即时编译的代码,因此可以把这一部分考虑为一个保存相对来说数据较为固定的部分,常量和静态变量在编译时就确定下来进入这部分内存,运行时类信息会直接加载到这部分内存,所以都是相对较早期进入内存的。
  • 运行时常量池:不是所有的常量都是在编译时就确定下来进入内存的,仍然会有运行时才进入内存的常量,这部分常量一般是编译时产生的一些固定信息,比如说翻译出的引用等,直接在类加载的时候把它们存入运行时常量池有助于提高性能。 所有的内存区域的数据交互由程序计数器指导虚拟机完成复杂的逻辑步骤。

如何找到一个对象的实例:

全选复制放进笔记

Object obj = new Object();

在这个过程中在虚拟机栈的局部变量表里创建obj引用,在堆内存里创建Object类的一个实例,最后就是把obj引用和这个对象实例关联起来的问题了,另外,我们需要知道的是,不是所有的实例都完整地保存了所有的类的信息,一般共有的或者静态的类的数据将被保存在方法区中,独有的实例数据才会真的被保存在java堆里,因此每个引用必须同时找到关联它的实例数据和类数据。针对这个问题,有两个办法来做:

I. 引用存储的只是实例的句柄,句柄在堆的句柄池中,句柄中保存着到堆中真正实例的地址和到方法区中类数据的地址,这样就可以通过这个句柄可以找到这些地址。

II. 引用存储的就是实例在堆中的地址,而实例中是含有可以定位类数据的地址的,也就是通过找到的实例地址可以再去寻找它对应的类的数据。

两个和内存溢出相关的异常:

  • StackOverflowError:线程申请的栈深度大于虚拟机的规定值;
  • OutOfMemoryError:线程扩展增加的内存大于虚拟机的要求;

二、内存回收机制

虚拟机栈、本地方法栈和计数器大都是编译期确定的内存分配,在线程执行完毕后即会清理,内存回收相对比较容易。所以我们提到的内存回收大都是指堆内存的回收。我们通过如下几个问题来说明内存回收机制:

1. 什么样的堆内存是可以回收的呢?

什么样的堆内存是可以回收的呢?简而言之就是那些“没用”的内存,那么怎样的内存是“没用”的呢?即那些通过现有的指针(或称“引用”)条件下再也访问不到的内存对象。所以有这样的算法来描述无效的引用: (引用计数算法)每个对象都有一个被引用计数器,被引用一次计数器加1,引用被置空时减1,最终被引用计数器的值为0 的即是“无用”的内存对象,它占用的内存可以被回收。 (这个算法看起来好像没有问题,但是遭遇到循环引用的时候就会出现问题:如果同时将循环引用的双方置空,那么即使被引用计数器不为0也再也访问不到这些对象了,即发生了内存无故占用)。

这个过程体现了互相循环引用可能带来的问题,对象仍被引用但是已经不能被访问了,所以是这种算法的缺陷。

(根搜索算法)将由栈内存或方法区引用的对象作为GCRoots去构建引用链,如果能找到这个对象则说明这个对象能够访问其内存不能被回收,反之通过这些引用链找不到这个对象则说明已经是弃用的对象了,其内存是应该被回收的。(上面的互相循环引用的例子就可以解决了,因为这个问题里面虽然其被引用计数器的值不为0,但是已经没有GCRoots能够找到这些内存了,这个问题里的GC Roots是栈内存里的objA和objB,这两个栈内存里的引用被置空,因此引用链里没办法再找到对内存里的对象了。)

2. 确定了有哪些内存该被回收后GC机制是直接回收内存吗?

确定了有哪些内存该被回收后GC机制是直接回收内存吗?GC会给这些内存中的某些对象一次机会,就是那些重写过finalize方法的类的对象,GC会执行这个对象重写过的finalize方法,如果在这个方法中对象重新将自己链接给了某个引用使得这块内存区域重新可以被访问,那么GC就不会在这次回收它,但是,这个过程只能执行一次,下一次再被GC遇到的话就不会顾及这个finalize方法而是直接回收了,因此要注意重写的finalize方法只能执行一次。 这个是堆内存中对象的回收,在方法区里保存类信息和常量池的内存同样需要回收,这个过程相对来说更缓慢也并没有那么高效,因为一段时间内线程使用的类和常量池都比较稳定,只有当真的确认有类不再使用且不被反射使用的时候才会卸载类,当真的没有常量再被使用的时候才会释放常量池中不用的常量。

参见:http://segmentfault.com/a/1190000003834613

时间: 2024-10-26 05:45:48

Java虚拟机的相关文章

《深入理解Java虚拟机》笔记04 -- 并发锁

Java虚拟机在操作系统层面会先尽一切可能在虚拟机层面上解决竞争关系,尽可能避免真实的竞争发生.同时,在竞争不激烈的场合,也会试图消除不必要的竞争.实现这些手段的方法包括:偏向锁.轻量级锁.自旋锁.锁消除.锁膨胀等 1. 偏向锁 偏向锁是JDK1.6提出的一种锁优化方式.其核心思想是:如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,无需再进行相关的同步操作,从而节省了操作时间.如果在此之间有其他线程进行了锁请求,则锁退

Java虚拟机9:Java类加载机制

http://www.cnblogs.com/xrq730/p/4844915.html 前言 我们知道我们写的程序经过编译后成为了.class文件,.class文件中描述 了类的各种信息,最终都需要加载到虚拟机之后才能运行和使用.而虚拟机如何加载这些.class文件?.class文件的信息进入到虚拟机后会发生什么变 化?这些都是本文要讲的内容,文章将会讲解加载类加载的每个阶段Java虚拟机需要做什么事(加粗标红). 类使用的7个阶段 类从被加载到虚拟机内存中开始,到卸载出内存,它的整个生命周期

Java虚拟机(JVM)中的内存设置详解

在一些规模稍大的应用中,Java虚拟机(JVM)的内存设置尤为重要,想在项目中取得好的效率,GC(垃圾回收)的设置是第一步. PermGen space:全称是Permanent Generation space.就是说是永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候被放入该区域Heap space:存放Instance. GC(Garbage Collection)应该不会对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很

探讨Java虚拟机之虚拟机体系结构

自工作以上,程序因为代码越写越多,变得越来越臃肿,效率也会变得越来越低,于是我总喜欢不断去优化程序结构外,内存优化和性能调优. 要对Java程序进行内存优化和性能调优,需要了解虚拟机的内部原理,了解Java虚拟机的好处除了上述提及两点好处.从更深一点的技术层面上看,了解Java虚拟机的规范和实现,将更加有助于我们编写高效.稳定的Java代码.比如,假如了解Java虚拟机的内存模型,了解虚拟机的内存回收机制,那么我们就不会过分依赖它,而会在需要的时候显式的"释放内存"(Java代码不能显

探讨深入Java虚拟机之内存优化

上一篇我们讲述了Java虚拟机的体系结构和内存模型,那么我们就不得不说到内存泄露.大家都知道,Java是从C++的基础上发展而来的,而C++程序的很大的一个问题就是内存泄露难以解决,尽管Java的JVM有一套自己的垃圾回收机制来回收内存,在大多数的情况下并不需要java程序开发人员操太多的心,但也是存在泄露问题的,只是比C++小一点.比如说,程序中存在被引用但无用的对象:程序引用了该对象,但后续不会或者不能再使用它,那么它占用的内存空间就浪费了. 我们先来看看GC是如何工作的:监控每一个对象的运

Java虚拟机内存模型

1)程序计数器:线程私有 当线程数量超过CPU数量时,线程之间根据时间片轮询抢夺CPU资源,对于单核CPU来说,每一个时刻,只能有一个线程在运行,而其他线程必须被切换出去.因此,每个线程都有一个独立的程序计数器,用于记录下一条要运行的指令,各个线程之间的计数器互不影响,是一块线程的私有内存空间.当一个线程正在执行一个Java方法时,程序计数器记录正在执行的Java字节码地址,如果执行的是native方法,则计数器为空 2)Java虚拟机栈:线程私有 同Java线程同时间创建,用于保存方法的局部变

Java虚拟机工作原理详解

原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 [java] view plaincopy javac YourClassName.java 此时,你的java代码就被编译成字节码(.class).如果你是在Eclipse IDE或者其他开发工具中,你保存代码

jvm java虚拟机

jdk jre ==================================== # ./jre1.6.0_27-for-linux.bin # ls /usr/java/jre1.6.0_27/ # mv /usr/java/jre1.6.0_27/* /usr/java/ # vi profile27 export JAVA_HOME=/usr/javaexport PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=$JAVA_HOME/lib ==

Java虚拟机详解——JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考之前的系列文章,尤其是那篇:Java虚拟机详解04--GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制. 先把本文的目录画一个思维导图:(图的源文件在本文末尾) 一.Java引用的四种状态: 强引用:  用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. * 如果一个对象具有强引用,那垃圾回收器绝不会回收它*.当内存空间不足,Java虚拟机宁

Java内存区域 - 深入Java虚拟机读后总结

Java虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,有各自的创建时间和销毁时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动和结束进行建立或销毁.Java虚拟机第二版规定,虚拟机管理的内存包含以下几个运行时数据区域 程序计数器:程序计数器(Program Counter Register)是一块较小的内存空间,作用可以理解为是当前线程所执行的字节码的行号指示器.Java虚拟机的多线程是通过线程切换以及分配处理器执行时间来