java 虚拟机学习笔记整理001--运行时的数据区域+垃圾收集算法
来源:互联网 发布:淘宝直通入门教程视频? 编辑:程序博客网 时间:2024/06/06 03:22
1.java的内存区域与内存溢出异常
-------------------------------------------------
以下是他人总结,大意差不多。
1. Java JVM:垃圾回收(GC 在什么时候,对什么东西,做了什么事情)?
在什么时候
GC又分为minor GC 和 Full Gc(也称为Major GC)。
Java 堆内存分为新生代和老年代,新生代中又分为1个Eden区域 和两个 Survivor区域。
1. 对于 Minor GC 的触发条件:大多数情况下,直接在 Eden 区中进行分配。如果 Eden区域(新生代)没有足够的空间,
那么就会发起一次 Minor GC;
2. 对于 Full GC(Major GC)的触发条件:也是如果老年代没有足够空间的
话,那么就会进行一次 Full GC。
Ps:上面所说的只是一般情况下,实际上,需要考虑一个空间分配担保的问题:
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于则进行Minor GC,
如果小于则看HandlePromotionFailure设置是否允许担保失败(不允许则直接Full GC)。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试Minor GC(如果尝试失败也会触发Full GC),如果小于则进行Full GC。
但是,具体到什么时刻执行,这个是由系统来进行决定,是无法预测的。
对什么东西
主要根据可达性分析算法,如果一个对象不可达,那么就是可以回收的;如果一个对象可达,那么这个对象就不可以回收。对于可达性分析算法,它是通过一系列称为“GC Roots” 的对象作为起始点,当一个对象到 GC Roots 没有任何引用链相接的时候,那么这个对象就是不可达,就可以被回收。如下图:
这个GC Root 对象可以是一些静态的对象,Java方法的local变量或参数, native 方法引用的对象,活着的线程。
做了什么事情
主要做了清理对象,整理内存的工作。Java堆分为新生代和老年代,采用了不同的回收方式。
例如新生代采用了复制算法,老年代采用了标记整理法。
在新生代中,分为一个Eden 区域和两个Survivor区域,真正使用的是一个Eden区域和一个Survivor区域,GC的时候,会把存活的对象放入到另外一个Survivor区域中,然后再把这个Eden区域和Survivor区域清除。
那么对于老年代,采用的是标记整理法,首先标记出存活的对象,然后再移动到一端。这样也有利于减少内存碎片。
JDK7 整体内存结构
摘录自《深入理解Java虚拟机》,针对 JDK7 的整体结构图如下:
如图,JVM内存区域分为 PC寄存器,JVM方法栈,本地方法栈,JVM方法区,JVM堆。
- PC寄存器:存放下一条指令在方法中的偏移量。也可以看做是线程所执行的字节码的行号指示器,字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的指令。
- JVM虚拟机栈:PC寄存器,JVM的虚拟机栈,和本地方法栈都是线程私有。JVM虚拟机栈主要由栈帧来组成,每一个方法的调用就相当于一个栈帧,所以说,当出现无限递归这种情况的时候,栈帧就可能会过多的创建,从而导致栈内存溢出。栈帧中又包含了局部变量区,操作数区等,用于具体对数据进行操作。
- 本地方法栈:主要用来支持native方法,记录native方法调用的状态。可以把native 方法看成是 java 调用 非 java 代码的一个接口。主要用于允许Java 和其他语言,比如 C 语言进行交互。
- JVM方法区:主要存储已经加载的类的信息,比如构造函数的信息,方法的信息,常量的信息。Class对象提供的getXXX()方法取得的类的信息就是从JVM方法区中得到。Ps:JVM方法区是永久代的一个子集,常量池也是放在JVM方法区中。
- JVM堆:主要目的是用来存放数组和对象。同时,JVM 堆也是 内存溢出和垃圾回收的主要区域。
JDK7 堆内存结构
堆内存结构图如下:
如图,在JVM堆中,又分为新生代,老生代。
可以看到,新生代中,又分为eden区域和两个Survivor区域。默认比例为8:1:1,也就是说,可以用的内存为90%。
当然,可以用-XX:SurvivorRatio设置eden和Survivor的比值,默认为8:1。
Ps:这样的一个分类也有利于垃圾收集的复制算法。
JDK8 内存结构变动
在JDK8中,最主要的就是元空间代替了永久代(PermGen Space),由于 上面结构图中的 JVM方法区是永久代的子集,那么就是说这部分会没有了,取而代之的是元空间(Metaspace)。这里有一篇比较好的文章介绍了元空间:点这里。
主要的意义在于:Metaspace 的内存大小可以动态增长,仅受限于本地内存大小。 当然,-XX:MetaspaceSize
和 -XX:MaxMetaspaceSize
可以设定大小。
JVM的栈与堆:
Java中的堆栈: https://my.oschina.net/u/1464779/blog/225590
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,因此,用堆保存数据时会得到更大的灵活性。变量所需的存储空间只有在运行时创建了对象之后才能确定。Java的垃圾收集器会自动收走这些不再使用的数
据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和引用对象,栈里存的却是堆的首地址名;就像引用变量。
Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用变量)而已。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)
1.在Java语法层面上创建一个对象,使用一个简单的new关键字即可,但是在JVM中细节的实现相当复杂,而且过程繁多。
2.当Java语法层面使用new关键字创建一个Java对象时,JVM首先会检查相对应的类是否已经成功经历加载、解析和初始化等步骤;当类完成装载步骤之后,就已经完全确定出创建对象实例时所需的内存空间大小,才能对其进行内存分配,以存储所生成的对象实例。
3.实例化之后,进行初始化(初始化对象头和实例数据)。
4.内存分配方式有:指针碰撞(Bump the Pointer)、快速分配策略、空闲列表(Free List)。
5.在并发环境下从堆中划分内存空间是非线程安全的,new运算符具有-------数据操作的原子性;也就是说创建一个Java对象分配内存,要么所有步骤都成功,返回对象的引用,要么回归到创建之前的内存状态,返回为NULL。
6.通过new创建一个Java对象,如果成功则返回这个对象的引用,开发者不可直接操作对象实例,需要通过这个引用“牵引”。
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
表示str还没有被“new”,就是对象的引用还没有创建,也就是还没有分配内存给他。此时不能用比较的函数进行操作,如if(str.equals("")),会抛出空指针异常。if("".equals(str))不会抛出异常,返回false。str!=""返回true,str ==null返回true。
System.out.println(str);输出结果为:null。因为null值可以强制转换为任何java类类型,输出时null值被转换成String类型。
String str ; String str =new String()。
表示str已经被“new”了,只是内容为空。str不是一个空引用,要分配内存空间。
第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
- java 虚拟机学习笔记整理001--运行时的数据区域+垃圾收集算法
- java运行时数据区域和垃圾收集算法
- Java虚拟机学习笔记(2)——垃圾收集算法
- 《深入理解java虚拟机》学习笔记04-垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习 - 垃圾收集算法
- Java虚拟机学习----垃圾收集算法
- Java虚拟机学习笔记2-垃圾收集
- 深入Java虚拟机笔记---垃圾回收算法,Java垃圾收集
- Java虚拟机的垃圾收集算法
- JqGrid 框架自带sum合计浮点数精度问题处理
- Spark 入门实战之最好的实例
- 利用HQL统计: 新增用户数,日活,留存率
- Spring Ioc和Aop原理
- node 链接mysql 数据库及增删改查操作
- java 虚拟机学习笔记整理001--运行时的数据区域+垃圾收集算法
- 挑战练习题2.3动态规划 poj3046 Ant Counting dp
- poj2255树的重建问题
- leetcode 30. Substring with Concatenation of All Words
- java.util.ResourceBundle 和java.util.properties 读取配置文件区别
- 通过ActionContext获取Map类型的request、session和application
- Javascript 中的 && 和 || 使用小结
- 世纪末的星期
- cscope 加入路径 全局路径