使用java操作MongoDB
前言
这一节我们来简单的介绍垃圾收集器,并学习垃圾标记的算法:引用计数算法和根搜索算法,为了更好的理解根搜索算法,会在文章的最后介绍Java对象在虚拟机中的生命周期。
1.垃圾收集器概述
垃圾收集器(Garbage Collection),通常被称作GC。提到GC,很多人认为它是伴随Java而出现的,其实GC出现的时间要比Java早太多了,它是1960诞生于MIT的Lisp。
GC主要做了两个工作,一个是内存的划分和分配,一个是对垃圾进行回收。关于内存的划分和分配,目前Java虚拟机内存的划分是依赖于GC的的设计的,比如现在GC都是采用了分代收集算法来回收垃圾,Java堆作为GC主要管理的区域,被细分为新生代和老年代,再细致一点新生代又可以划分为Eden空间、From Survivor空间、To Survivor空间等,这样进行划分是为了更快的进行内存分配和回收。空间划分后,GC就可以为新对象分配内存空间。
关于对垃圾进行回收,被引用的对象是存活的对象,而不被引用的对象是死亡的对象也就是垃圾,GC要区分出存活的对象和死亡的对象,也就是垃圾标记,并对垃圾进行回收。接下来我们先来介绍垃圾标记算法。
2.垃圾标记算法
在对垃圾进行回收前,GC要先标记出垃圾,那么如何标记呢,目前有两种垃圾标记算法,分别是引用计数算法和根搜索算法,这两个算法都和引用有些关联,因此讲垃圾标记算法前,我们先回顾下引用的知识。
引用
在JDK1.2之后,Java将引用分为强引用、软引用、弱引用和虚引用。
- 强引用:当我们new一个对象时就是创建了一个具有强引用的对象,如果一个对象具有强引用,垃圾收集器就绝不会回收它。Java虚拟机宁愿抛出OutOfMemoryError异常,使程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题。
- 软引用:如果一个对象只具有软引用,当内存不够时,会回收这些对象的内存,回收后如果还是没有足够的内存,就会抛出OutOfMemoryError异常。Java提供了SoftReference类来实现软引用。
- 弱引用:弱引用比起软引用具有更短的生命周期,垃圾收集器一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收它的内存。Java提供了WeakReference类来实现弱引用。
- 虚引用:虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,这就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。一个只具有虚引用的对象,被垃圾收集器回收时会收到一个系统通知,这也是虚引用的主要作用。Java提供了PhantomReference类来实现虚引用。
引用计数算法
引用计数算法的基本思想就是每个对象都有一个引用计数器,当对象在某处被引用的时候,它的引用计数器就加1,引用失效时就减1。当引用计数器中的值变为0,则该对象就不能被使用成了垃圾。
目前主流的Java虚拟机没有选择引用计数算法来为垃圾标记,主要原因是引用计数算法没有解决对象之间相互循环引用的问题。
举个例子,下面代码的注释1和注释2处,d1和d2相互引用,除此之外这两个对象无任何其他引用,实际上这两个对象已经死亡,应该作为垃圾被回收,但是由于这两个对象互相引用,引用计数就不会为0,垃圾收集器就无法回收它们。
class _2MB_Data {
public Object instance = null;
private byte[] data = new byte[ * * ];//用来占内存,测试垃圾回收
}
public class ReferenceGC {
public static void main(String[] args) {
_2MB_Data d1 = new _2MB_Data();
_2MB_Data d2 = new _2MB_Data();
d1.instance = d2;//
d2.instance = d1;//
d1 = null;
d2 = null;
System.gc();
}
}
如果你使用Android Studio,就在Edit Configurations中的VM options加入如下语句来输出详细的GC日志:
-XX:+PrintGCDetails
运行程序,GC日志为:
[GC (System.gc()) [PSYoungGen: 8028K->832K(76288K)] 8028K->840K(251392K), 0.0078334 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 832K->0K(76288K)] [ParOldGen: 8K->603K(175104K)] 840K->603K(251392K), [Metaspace: 3015K->3015K(1056768K)], 0.0045844 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 76288K, used 1966K [0x000000076af80000, 0x0000000770480000, 0x00000007c0000000)
eden space 65536K, % used [0x000000076af80000,0x000000076b16bac0,0x000000076ef80000)
from space 10752K, % used [0x000000076ef80000,0x000000076ef80000,0x000000076fa00000)
to space 10752K, % used [0x000000076fa00000,0x000000076fa00000,0x0000000770480000)
ParOldGen total 175104K, used 603K [0x00000006c0e00000, 0x00000006cb900000, 0x000000076af80000)
object space 175104K, % used [0x00000006c0e00000,0x00000006c0e96d10,0x00000006cb900000)
Metaspace used 3046K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 334K, capacity 388K, committed 512K, reserved 1048576K 查看此GC日志前我们先来简单了解下各参数的含义,[GC (System.gc()和[Full GC (System.gc()说明了这次垃圾收集的停顿类型,而不是来区分新生代GC和老年代GC的。 [Full GC (System.gc() 说明这次GC发生了STW,STW也就是Stop the World机制,意思是说在执行垃圾收集算法时,只有GC线程在运行,其他的线程则会全部暂停,等待GC线程执行完毕后才能再次运行。
PSYoungGen代表新生代,ParOldGen代表老年代,Metaspace代表元空间(JDK 8中用来替代永久代PermGen)。
我们来看日志的[GC (System.gc()),内存变化为:8028K->840K(251392K),8028K代表回收前的内存大小,840K代表回收后的内存大小,251392K代表内存总大小。因此可以得知内存回收大小为(-)K。这就说明JDK8的HotSpot虚拟机并没有采用引用计数算法来标记内存,它对上述代码中的两个死亡对象的引用进行了回收。
根搜索算法
这个算法的基本思想就是选定一些对象作为GC Roots,并组成根对象集合,然后从这些作为GC Roots的对象作为起始点,向下进行搜索,如果目标对象到GC Roots是连接着的,我们则称该目标对象是可达的,如果目标对象不可达则说明目标对象是可以被回收的对象,如下图所示。 
从上图看以看出,Obj5、Obj6和Obj7都是不可达的对象,其中Obj5和Obj6虽然互相引用,但是因为他们到GC Roots是不可达的所以它们仍旧会判定为可回收的对象,这样根搜索算法就解决了引用计数算法无法解决的问题:已经死亡的对象因为相互引用而不能被回收。
在Java中,可以作为GC Roots的对象主要有以下几种:
- Java栈中的引用的对象。
- 本地方法栈中JNI引用的对象。
- 方法区中运行时常量池引用的对象。
- 方法区中静态属性引用的对象。
- 运行中的线程
- 由引导类加载器加载的对象
- GC控制的对象
还有一个问题是被标记为不可达的对象会立即被垃圾收集器回收吗?要回答这个问题我们首先要了解Java对象在虚拟机中的生命周期。
3.Java对象在虚拟机中的生命周期
当Java对象被类加载器加载到虚拟机中后,Java对象在Java虚拟机中有7个阶段。
1.创建阶段(Created)
创建阶段的具体步骤为:
- 为对象分配存储空间。
- 构造对象。
- 从超类到子类对static成员进行初始化。
- 递归调用超类的构造方法。
- 调用子类的构造方法。
2.应用阶段(In Use)
当对象被创建,并分配给变量赋值,状态就切换到了应用阶段。
这一阶段的对象至少要具有一个强引用,或者显式的使用软引用、弱引用或者虚引用。
3.不可见阶段(Invisible)
程序中找不到对象的任何强引用,比如程序的执行已经超出了该对象的作用域。在不可见阶段,对象仍可能被特殊的强引用GC Roots持有着,比如对象被本地方法栈中JNI引用或是被运行中的线程引用等。
4.不可达阶段(Unreachable)
程序中找不到对象的任何强引用,并且垃圾收集器发现对象不可达。
5.收集阶段(Collected)
垃圾收集器已经发现对象不可达,并且垃圾收集器已经准备好要对该对象的内存空间重新进行分配时。这个时候如果该对象重写了finalize方法,则会调用该方法。
6.终结阶段(Finalized)
当对象执行完finalize法后仍然处于不可达状态时,或者对象没有重写finalize方法,则该对象进入终结阶段,并等待垃圾收集器回收该对象空间。
7.对象空间重新分配阶段(Deallocated)
当垃圾收集器对对象的内存空间进行回收或者再分配时,这个对象就会彻底消失。
好了,我们已经了解了Java对象在虚拟机中的生命周期,再来回想我方才说的问题:被标记为不可达的对象会立即被垃圾收集器回收吗?很显然是不会的,被标记为不可达的对象会进入收集阶段,这时会执行该对象重写的finalize方法,如果没有重写finalize方法或者finalize方法中没有重新与一个可达的对象进行关联才会进入终结阶段,并最终被回收。
JVM 深入笔记(3)垃圾标记算法
GC roots
Java GC - 监控回收行为与日志分析
Java:对象的强、软、弱和虚引用
JVM GC中Stop the world案例实战
Java对象的生命周期
使用java操作MongoDB的更多相关文章
- 【MongoDB for Java】Java操作MongoDB
上一篇文章: http://www.cnblogs.com/hoojo/archive/2011/06/01/2066426.html介绍到了在MongoDB的控制台完成MongoDB的数据操作,通过 ...
- JAVA操作MongoDB数据库
1. 首先,下载MongoDB对Java支持的驱动包 驱动包下载地址:https://github.com/mongodb/mongo-java-driver/downloads 2.Java操作Mo ...
- [转]MongoDB for Java】Java操作MongoDB
原文地址: MongoDB for Java]Java操作MongoDB 开发环境: System:Windows IDE:eclipse.MyEclipse 8 Database:mongoDB 开 ...
- Java操作MongoDB
上一篇文章: http://www.cnblogs.com/hoojo/archive/2011/06/01/2066426.html 介绍到了在MongoDB的控制台完成MongoDB的数据操作,通 ...
- Mongodb快速入门之使用Java操作Mongodb
[IT168 专稿]在上一篇文章中,我们学习了Mongodb的安装和初步使用,在本文中,将学习如何使用Java去编程实现对Mongodb的操作. HelloWorld程序 学习任何程序的第一步,都是编 ...
- Mongodb入门并使用java操作Mongodb
转载请注意出处:http://blog.csdn.net/zcm101 最近在学习NoSql,先从Mongodb入手,把最近学习的总结下. Mongodb下载安装 Mongodb的下载安装就不详细说了 ...
- 浅谈如何用Java操作MongoDB
NoSQL数据库因其可扩展性使其变得越来越流行,利用NoSQL数据库可以给你带来更多的好处,MongoDB是一个用C++编写的可度可扩展性的开源NoSQL数据库.本文主要讲述如何使用Java操作Mon ...
- java操作mongodb(连接池)(转)
原文链接: java操作mongodb(连接池) Mongo的实例其实就是一个数据库连接池,这个连接池里默认有10个链接.我们没有必要重新实现这个链接池,但是我们可以更改这个连接池的配置.因为Mong ...
- Java操作Mongodb(转载)
好文章,值得收藏 HelloWorld程序 学习任何程序的第一步,都是编写HelloWorld程序,我们也不例外,看下如何通过Java编写一个HelloWorld的程序. 首先,要通过Java操作Mo ...
- java 操作mongodb查询条件的常用设置
java操作mongodb进行查询,常用筛选条件的设置如下: 条件列表:BasicDBList condList = new BasicDBList(); 临时条件对象:BasicDBObject c ...
随机推荐
- JSON数据的定义
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 深入理解DOM节点类型第三篇——注释节点和文档类型节点
× 目录 [1]注释节点 [2]文档类型 前面的话 把注释节点和文档类型节点放在一起是因为IE8-浏览器的一个bug.IE8-浏览器将标签名为"!"的元素视作注释节点,所以文档声明 ...
- JS验证控件jQuery Validate
jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证 ...
- 窥探Swift之数组与字典
说到数组和字典,只要是编过程的小伙伴并不陌生.在Swift中的数组与字典也有着一些让人眼前一亮的特性,今天的博客就来窥探一下Swift中的Array和Dictionary.还是沿袭之前的风格,在介绍S ...
- CDH安装失败了,如何重新安装
1> 删除Agent节点的UUID # rm -rf /opt/cm-5.4.7/lib/cloudera-scm-agent/* 2> 清空主节点CM数据库 进入主节点的Mysql数据 ...
- Lua 学习笔记(九)协同程序(线程thread)
协同程序与线程thread差不多,也就是一条执行序列,拥有自己独立的栈.局部变量和命令指针,同时又与其他协同程序共享全局变量和其他大部分东西.从概念上讲线程与协同程序的主要区别在于,一个具有多个线程的 ...
- C#将一个excel工作表根据指定范围拆分为多个excel文件
C#将一个excel工作表根据指定范围拆分为多个excel文件 微软Excel没有提供直接的方法来拆分excel文件,因此要拆分一个excel文件最简单的方法可能就是手动剪切和粘贴了,除此之外,还有其 ...
- Selenium3.0 自动化测试
早在2013年的时候,Selenium官方宣布,Selenium新的版本会在圣诞节的时候发布.但是,他们并没有说哪一个圣诞节发布. 转眼的三年过去了,目前已经发布到Selenium3.0 beta4版 ...
- hibernate笔记--双向一对多映射方法
前两节写了两个例子,分别是单向多对一的映射和单向一对多的映射,这一节继续以这个例子讲一下双向一对多的映射方法,如下图所示: 很多时候,我们既想从一对端获取多对端的信息,又想从多对端获取一对端的数据,这 ...
- Ajax.BeginForm VS Html.BeginForm
有的人说,AJAX一听,高大上,HTML一听,死老土,所以AJAX更好.其实这是错误的.每种方法有它不同的用途.现在做如下总结: @using (Ajax.BeginForm("Login& ...