文档章节

虚拟机学习之二:垃圾收集器和内存分配策略

贾峰uk
 贾峰uk
发布于 2018/10/23 00:07
字数 5993
阅读 71
收藏 1

1.对象是否可回收

1.1引用计数算法

引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时候计数器值为0的对象就是不可能再被使用的对象。

客观来说,引用计数算法的实现简单,判定效率高,在大部分情况下都是不错的算法,但是在主流的java虚拟机里面都没有选用该算法进行内存管理,主要原因是它很难解决对象之间相互循环引用的情况。如下面代码例子:

配置:输出垃圾回收日志

-XX:+PrintGC

 

代码: 

public class ReferenceCountingGC {

	private ReferenceCountingGC instance = null;

	private static final int _1M = 1024 * 1024;

	private byte[] bsize = new byte[2 * _1M];

	public static void testGC() {
		ReferenceCountingGC rc1 = new ReferenceCountingGC();
		ReferenceCountingGC rc2 = new ReferenceCountingGC();
		
		//两个对象互相引用
		rc1.instance = rc2;
		rc2.instance = rc1;
		
		rc1 = null;
		rc2 = null;
		//提醒虚拟机执行垃圾回收
		System.gc();
	}
	
	public static void main(String[] args) {
		testGC();
	}

}

运行结果:

[GC (System.gc())  6092K->736K(125952K), 0.0009621 secs]
[Full GC (System.gc())  736K->612K(125952K), 0.0068694 secs]

 从运行结果中可以清楚看到,GC日志中包含6092K->736K,意味着虚拟机并没有因为这两个对象相互引用就不回收它们,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否或者。(配置-XX:+PrintGC或者-verbose:gc输出基本回收信息,配置-XX:+PrintGCDetails可以输出详细的GC信息)。

1.2可达性分析算法

在虚拟机的主流实现中,都是通过可达性分析算法来判定对象是否存活的。这个算法的基本思路就是:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(也就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

在java语言中可以作为“GC Roots”的对象包括以下几种:

  • 虚拟机栈中引用的对象,也就是栈帧中本地变量表中的对象。
  • 方法区中静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(一般说的是Native方法)引用的对象。

如下面图中所示:虽然obj5、obj6、obj7之间相互引用但是它们到GC Roots没有可达的调用链,所以他们将会被判定为可回收的对象。

1.3对象引用类别

在JDK1.2之前定义引用:如果reference类型的数据中存储的数值代表另一块内存的起始地址,就称这块内存代表着一个引用。这种定义虽然比较纯粹但是太过狭隘,我们实际中更希望能代表一种情况:当内存空间足够时,则保留在内存之中,当内存空间在进行垃圾回收之后依然比较紧张,则可以抛弃这些对象。所以在JDK1.2之后java就对引用概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。这四种引用强度依次减弱。

  • 强引用:这种引用在代码中普遍存在,类似“Object obj = new Object()”这类的引用只要引用还在,垃圾收集器永远不会回收掉被引用的对象。
  • 软引用:这种引用用来描述一些“还有用但并非必须”的对象,对于软引用关联的对象,在系统将要发生内存溢出之前,会将这些对象列入回收对象之中进行二次回收,如果回收之后依然没有足够的内存,才会抛出内存溢出异常。
  • 弱引用:是用来描述非必须对象的,但它的强度比软引用更弱一些,被若引用关联的对象只能生存到下一次垃圾回收之前,当垃圾收集器工作时,无论当前内存是否足够都会回收掉被弱引用关联的对象。
  • 虚引用:也被成为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用存在完全不会对其生存时间产生影响,也不能通过虚引用取得一个对象实例。为对象设置虚引用的唯一目的就是能够在对象被回收时收到一个通知。

1.4finalize方法

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候他们暂时处于“缓刑”阶段,要真正宣布一个对象死亡,至少要经理两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的调用链,那么这个对象将会被进行第一次标记并且进行一次筛选,筛选的条件就是是否有必要执行finalize方法,如果没有覆盖该方法或者已经被虚拟机调用过,就会被认为“没有必要执行”。如果被判定为有必要执行finalize方法,就会将对象放置在一个叫做“F-Queue”的队列中,并由一个虚拟机自建的优先级低的线程去执行它(虚拟机只是触发调用,并不保证执行成功或完成)。如果在执行finalize方法的过程中对象重新与引用链上的任何一个对象建立关联则在稍后的第二次标记中该对象就会被移除“即将回收队列”,如果这时候依然没有和引用链上的对象建立关联,则该对象就会被回收。虚拟机调用对象finalize方法只有一次,不会进行第二次调用。如下代码实例:

代码:

public class FinalizeEscapeGC {

	public static FinalizeEscapeGC SAVE_HOOK = null;
	
	public void isAlive(){
		System.out.println("yes,I am still alive!");
	}
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		//与成员变量建立关联
		System.out.println("finalize method executed!");
		FinalizeEscapeGC.SAVE_HOOK = this;
	}
	
	public static void main(String[] args) throws Exception {
		SAVE_HOOK = new FinalizeEscapeGC();
		//对象第一次拯救自己
		SAVE_HOOK = null;
		System.gc();
		//因为虚拟机调用finalize方法优先级比较低,暂停1s等待执行。
		Thread.sleep(1000);
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println("I am dead!");
		}
		
		//第二次时不会再调用finalize方法
		SAVE_HOOK = null;
		System.gc();
		Thread.sleep(1000);
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println("I am dead!");
		}
		
		
	}
}

执行结果:

finalize method executed!
yes,I am still alive!
I am dead!

可以看到回收的第一次执行了finalize方法然后对象没有被回收,第二次时没有调用finalize方法,对象被回收掉了。

这种方法虽然能在对象被回收时自救一次,但在编写代码时不建议使用此种操作。

1.5回收方法区(JDK8中是回收元空间)

按照JDK7介绍,永久代中的垃圾收集主要回收两部分内容:废弃常量和无用的类。

废弃常量:废弃常量的回收和java堆中对象的回收非常类似。以常量池为例,如果一个字符串“abc”已经进入常量池中,但是当前系统中没有任何一个String字符串对象叫做“abc”,也就是没有任何一个对象引用这个“abc”常量,也没有任何一个地方引用这个字面量。这个时候发生内存回收,有必要的话常量池中的“abc”常量会被系统清理出常量池。

无用的类:类的回收判定比较严格要满足一下三个条件才可以会被回收。

  • 该类所有的实例都已经被回收。
  • 加载该类的ClassLoader也已经被回收。
  • 该类对应的class对象也没有在任何地方被引用,也就是不能通过反射访问该类。

2.垃圾收集算法

2.1标记-清除算法

标记-清除算法:最基础的收集算法,主要分为“标记”和“清除”两个阶段完成,首先标记出所有需要回收的对象,在标记完成之后进行统一回收。之所以说它时最基础的收集算法是因为后续的收集算法都是基于这种思路进行对其不足进行改进而得到的。

这种算法有两种不足:第一个就是这两个阶段的效率都不高;第二个是在标记清除之后会产生大量的不连续的内存碎片,空间碎片太多可能会导致在后面程序运行过程中如果分配较大对象时,无法找到足够的连续内存空间,而不得不提前进行下一次垃圾回收。

2.2复制算法

复制算法:将可用内存分为大小相等的两部分,每次只使用其中的一块,当这一块内存用完,就进行垃圾回收将还存活的对象复制到另一块上面,然后清除掉刚才使用的内存空间。这样做的好处就是每次对整个板块内存进行回收,不用考虑内存碎片等复杂问题。但是这种算法的代价就是讲内存可用空间直接缩小了一半。

在商业虚拟机中都使用这种算法来回收新生代。IBM研究表明大部分情况新生代中有98%的对象会被第一次收集时被回收掉,所以在实现中把内存分为较大的一块Eden空间和两个较小的Survivor空间,比例是8:1:1.每次使用时将新创建的对象分配到Eden区其中一个Surivivor区保存上次回收存活下来的对象,当进行垃圾收集时将Eden和使用中的Survivor中的存活对象复制到另一个Survivor中,然后清空Eden和使用过的Survivor空间,依次循环使用。当然并不是每次存活的对象都不足10%,当存活对象大于10%时Surivivor中的空间就不够使用,就需要依赖其他内存进行分配担保(老年代)。也就是当另一块Surivivor内存不够时就会将存活的对象分配到老年代中。

2.3标记-整理算法

复制算法在对象存活率较高时就要进行较多的复制操作,从而降低效率。还要预留担保空间,以应对存活对象较多时新生代内存不够分配的情况。所以在老年代提出了“标记-整理”算法,标记同样跟前面的“标记-清除”算法中标记操作一样,但是标记之后不会将对象清除掉,而是将对象移动到整块内存空间的一端,然后直接清理掉边界以外的内存。

2.4分代收集算法

当前商业虚拟机都采用“分代收集算法”,这种算法只是将整块内存按照对象存活周期分为几个块,一般把java堆分为新生代、老年代。这样就可以根据各个年代特点使用不同算法进行收集。例如在新生代每次回收时都有少量对象可以存活,就是用复制算法,将少量存活对象复制到Survivor区。而老年代对象存活率比较高只有少量对象会被清除掉,就选用“标记-清除”或者“标记-整理”算法。

3.HotSpot算法实现

3.1枚举根节点

在可达性分析算法中可以作为GC Roots节点的主要在全局性引用(例如常量、静态属性)或者执行上下文(栈中本地变量表)中。在查找调用链的时候并不会这个检查这里面的引用,因为这样会消耗很多时间。

HotSopt实现中,使用一组称为OopMap的数据结构,在类加载完成的时候就已经计算出来对象“哪些”偏移量上面存储“哪些”数据类型。例如:在JIT编译过程中会在特定位置记录栈和寄存器中哪些位置是引用。这样在GC扫描的时候可以直接引用。

另外在执行GC 的时候所有java线程都必须停顿下来(Stop The World),因为在执行可达性分析算法的时候对象的引用关系不能发生变化。

3.2安全点

上面提到记录引用的特定位置称为“安全点”,线程在GC的时候需要暂停执行,但并不是在任何地方都可以停下来的,需要线程跑到“安全点”上时如果这时候有GC标识就暂停执行,这样可以保证在GC时引用不会发生变化。

3.3安全区域

安全区域:指在一段代码片段之中,引用关系不会发生变化。这个区域中的任何位置开始GC 都是安全的。

4.垃圾收集器

4.1Serial收集器

新生代收集器,复制算法。

Serial收集器是最基本、发展历史最悠久的收集器。这个收集器是一个单线程的收集器,它有一条专门的线程负责垃圾收集工作,更重要的是它在垃圾回收的时候要停止所有其他线程。主要用在Client模式下(用户的桌面场景中)。

4.2ParNew收集器

新生代收集器,复制算法。

ParNew收集器是Serial收集器的多线程版本。除了使用多线程进行垃圾收集之外其他的所有包括控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial完全一样。

4.3Paralle Scavenge收集器

Parallel Scavenge收集器是一个新生代收集器,也是使用复制算法的收集器,有事并行的多线程收集器。

Paralle Scavenge收集器的特点就是关注吞吐量:运行用户代码时间 / CPU总运行时间(用户代码时间+垃圾收集时间)。

Paralle Scavenge收集器提供了可以配置精准控制吞吐量的参数。所以又称为“吞吐量优先”收集器。

4.4Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,同样是一个单线程收集器,使用“标记-整理”算法。

主要给client模式下的虚拟机使用。

4.5Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。和Paralle Scavenge收集器搭配实现名副其实的“吞吐量优先”收集器。

4.6CMS收集器

CMS收集器(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器。CMS收集器主要分四个步骤:

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清除

初始标记、重新标记:这两个步骤虽然很快但是还是需要“Stop The World”。

并发标记:进行GC Roots tracing,在这个阶段jvm收集线程会和用户线程并行执行。(时间较长,降低用户系统信息)。

缺点:

  • 占用用户CPU资源,4核以上服务器至少占用1/4CPU资源。
  • 产生“浮动垃圾”由于CMS收集器和用户线程并发执行,在收集过程中用户线程可能产生新的垃圾对象。
  • 标记清除算法产生碎片内存空间,多次执行标记清除回收之后要进行一次内存压缩。

4.7G1收集器

G1收集器是当今收集技术最前沿成果之一。

特点:

  • 并行和并发,缩短“Stop The World”时间,让用户线程和收集线程并发执行。
  • 分代收集
  • 空间整合,不会产生内存碎片。
  • 可预测停顿时间,可以让使用者明确指定在一个长度为M毫米的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

G1之前的收集器收集范围都是整个新生代或者老年代。而G1收集器将整个java堆划分为多个大小相等的独立区域(Region),虽然还保留着新生代和老年代,但新生代和老年代不再是物理隔离的了,他们都是有一部分Region的集合组成。G1维护一个优先列表记录每个Region回收的价值大小,每次根据允许收集时间,首先回收价值最大的Region。

4.8理解GC日志

JVM中可以配置使用不同的收集器,不同收集器输出的日志格式虽然相同,但每种收集器都有自己的标识。

例如:

1、配置:"-XX:+UseSerialGC" 使用Serial+Serial Old的收集器组合进行内存回收。

日志格式:[GC (Allocation Failure) [DefNew: 7292K->612K(9216K), 0.0055084 secs] 7292K->6756K(19456K), 0.0055700 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

2、配置:"-XX:+UseParallelOldGC" 使用Paralle Scavenge + Parallel Old的收集器组合进行内存回收。

日志格式:[GC (Allocation Failure) --[PSYoungGen: 7292K->7292K(9216K)] 11388K->15492K(19456K), 0.0030434 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 7292K->2658K(9216K)] [ParOldGen: 8200K->8193K(10240K)] 15492K->10851K(19456K), [Metaspace: 2664K->2664K(1056768K)], 0.0078503 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 

JDK8 HotSopt虚拟机默认使用的是并行收集器,日志如下格式进行讲解。

[GC (System.gc()) [PSYoungGen: 1331K->32K(38400K)] 1943K->644K(125952K), 0.0004471 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 32K->0K(38400K)] [ParOldGen: 612K->611K(87552K)] 644K->611K(125952K), [Metaspace: 2662K->2662K(1056768K)], 0.0076396 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 

[GC 和[Full GC 代表收集停顿来下,Full表示有停顿及“Stop The World”。

[PSYongGen 和 [ParOldGen、[Metaspace表示GC发生的区域,[PSYongGen新生代;[ParOldGen老年代;[Metaspace元空间。

区域后面“[ ]”之内的32K->0K(38400K) 表示:该区域回收之前占用容量->回收之后占用容量(该区域总容量)。

"[ ]"之外的644K->611K(125952K)表示:java堆GC之前的占用容量->GC之后占用容量(java堆总用量)。

5内存分配与回收策略

5.1对象优先在Eden分配

通过例子讲解:首先创建4个数组对象allocation1、allocation2、allocation3、allocation4,占用空间分别为2M、2M、2M、4M,然后指定虚拟机堆内存20M,新生代内存10M,新生代中Eden区域Survivor区域占比为8:1。

public class EdenTest {

	private static final int _1MB = 1024 * 1024;
    /**
	 * -verbose:gc -Xms20M(堆初始大小) -Xmx20M(堆最大值) -Xmn10M(堆中年轻代大小) -XX:+PrintGCDetails -XX:SurvivorRatio=8(表示Eden与一个Survivor比例为8:1)
	 */
	private static void testAllocation() {
		byte[] allocation1, allocation2, allocation3, allocation4;
		allocation1 = new byte[2 * _1MB];
		allocation2 = new byte[2 * _1MB];
		allocation3 = new byte[2 * _1MB];
		allocation4 = new byte[4 * _1MB];
	}

	public static void main(String[] args) {
		testAllocation();
	}

}

使用Serial+Serial Old收集器组合进行内存回收(UseSerialGC配置指定收集器)

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC

运行日志:

[GC (Allocation Failure) [DefNew: 7292K->613K(9216K), 0.0050167 secs] 7292K->6757K(19456K), 0.0050693 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4791K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  from space 1024K,  59% used [0x00000000ff500000, 0x00000000ff599460, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
 Metaspace       used 2668K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

日志解读

GC (Allocation Failure):表示向young generation(eden)给新对象申请空间,但是young generation(eden)剩余的合适空间不够所需的大小导致的minor gc。

DefNew:表示新生代使用Serial串行GC垃圾收集器,defNew提供新生代空间信息。

7292K->613K(9216K):新生代占用内存7292K -> 收集器回收之后占用内存613K(新生代可用内存9216K)。

7292K->6757K(19456K):java堆被占用内存7292K -> 收集器回收之后占用内存6757K (堆内存总空间19456K)。

Heap则表示此时堆内存中每个区域分配内存大小以及被使用的空间比例。

下面我们通过日志分析allocation1、allocation2、allocation3、allocation4这四个对象分配的位置。

首先日志的第一行进行新生代收集出现日志:7292K->613K(9216K),说明allocation1、allocation2、allocation3这三个对象共计6M大小在Eden区,收集过后使用空间为613K,对象被移走,正常情况下Eden区对象首次会被移到其中一个Survivor区,但是Survivor区空间只用1M不足存放6M对象大小,所以这些对象直接被移送到了老年代中。堆内存收集前后并没有发生大的变化7292K->6757K(19456K),也表示这些对象还在堆内存中,只是从新生代的Eden区直接被移送到了老年代中。

从最后堆内存各个区域内存占用的情况也可以分析得出我们的推论

区域 大小 使用比例 说明
新生代:eden 8192K 51% 被占用4M空间,被allocation4占用
新生代:from 1024K 59% 空间不足1M,并未存放测试对象
新生代:to   1024K 0% Survivor只有一个被使用
老年代区域 10240K 60%  老年代占用6M,为3个2M的对象

5.2大对象直接进入老年代

大对象就是在虚拟机中需要大量连续空间存放的对象。比如字符串对象或数组等。

虚拟提供一个-XX:PretenureSizeThreshold参数,可以设置令大于该参数值的对象直接进入老年代。这个参数只对Serial和ParNew收集器有效。

public class PretenureSizeThresholdTest {

	private static final int _1MB = 1024 * 1024;

	/**
	 * -verbose:gc -Xms20M(堆初始大小) -Xmx20M(堆最大值) -Xmn10M(堆中年轻代大小)
	 * -XX:+PrintGCDetails -XX:SurvivorRatio=8(表示Eden与一个Survivor比例为8:1)
	 * -XXPretenureSizeThreshold=3145728(超过该值的对象直接进入老年代)
	 */
	public static void main(String[] args) {

		byte[] allocation;
		allocation = new byte[4 * _1MB];
	}
}

JVM执行参数配置:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:PretenureSizeThreshold=3145728

执行日志:

Heap
 def new generation   total 9216K, used 1312K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  16% used [0x00000000fec00000, 0x00000000fed480b0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 2670K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

其中the space 10240K,  40% used,老年代使用40%,说明对象直接进入了老年代区域。

5.3长期存活的对象将进入老年代

       虚拟机采用分代收集的思想来管理内存,虚拟机为每个对象定义了一个年龄(Age)计数器,如果对象在Eden出生,并经历一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。此后对象在Survivor中每熬过一次Minor GC对象年龄就增加1岁,当年龄增加到一定程度时(默认15岁),该对象就会被移动到老年代中。对象晋升到老年代的这个阈值可以通过参数设定 -XX:MaxTenuringThreshold=2,表示对象经理过两次Minor GC 就可以被移动到老年代。

5.4动态对象年龄判定

对象移动到老年代的另一个规则:当Survivor空间中相同年龄的对象所占用空间大小的总和大于Survivor空间的一半时,年龄大于或等于该年龄的对象就可以被直接移动到老年代中。

© 著作权归作者所有

共有 人打赏支持
贾峰uk
粉丝 2
博文 89
码字总数 139027
作品 0
深圳
私信 提问
聊聊JAVA虚拟机中的垃圾收集器

前言 JAVA虚拟机的垃圾收集器是虚拟机内存的清道夫,它的存在让JAVA开发人员能将更多精力投入到业务研发上。了解垃圾收集器,并利用好这个工具,能更好的保障服务稳定性。这篇文章通过分析J...

lilugoodjob
2018/07/02
0
0
深入学习Java虚拟机——垃圾收集器与内存分配策略

垃圾回收操作的步骤:首先确定对象是否死亡,然后进行回收 1. 如何判断对象是否死亡 1.1 引用计数法 1.引用计数法:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引...

江左煤郎
2018/08/10
0
0
垃圾收集器与内存分配策略-深入理解jvm

最近学习了周志明老师的《深入理解Java虚拟机》,收获颇多,留下一些学习笔记,供以后复习用。 一.学习目标 1.对象存活判断 2.GC(garbage collection)算法学习 3.垃圾回收器 4.内存分配与回...

Swen_9826
2018/08/09
0
0
Java虚拟机基础——4内存回收机制

Java虚拟机整体篇幅如下: Java虚拟机基础——1Java的内存模型 Java虚拟机基础——2JVM运行时数据区 Java虚拟机基础——3类加载机制 Java虚拟机基础——4内存回收机制 本篇文章的内容如下: ...

隔壁老李头
2018/10/03
0
0
JAVA高级面试总结-JVM篇

1.Sun HotSpot VM,是JDK和Open JDK中自带的虚拟机,也是目前使用范围最广的Java虚拟机。 2.JVM内存分布 程序计数器:是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。...

t4i2b10x4c22nf6a
2017/12/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

node调用dll

先安装python2.7 安装node-gyp cnpm install node-gyp -g 新建一个Electron-vue项目(案例用Electron-vue) vue init simulatedgreg/electron-vue my-project 安装electron-rebuild cnpm ins......

Chason-洪
今天
3
0
eclipse中项目svn转gitLab全过程

在工作中,我们可能会遇到项目从svn迁移到gitLab;此过程我们需要变化版本管理工具,上传代码。本篇博客记录了使用spring tool suit(sts/eclipse)进行项目迁移的全过程。 步骤: (1)端口之...

em_aaron
今天
3
0
scala学习(一)

学习Spark之前需要学习Scala。 参考学习的书籍:快学Scala

柠檬果过
今天
1
0
通俗易懂解释网络工程中的技术,如STP,HSRP等

导读 在面试时,比如被问到HSRP的主备切换时间时多久,STP几个状态的停留时间,自己知道有这些东西,但在工作中不会经常用到,就老是记不住,觉得可能还是自己基础不够牢固,知识掌握不够全面...

问题终结者
昨天
4
0
看了一下Maven的内容

了解了Maven其实是一个跨IDE的标准构建工具,能推广的原因估计是借了仓库的便利。 另一个作用是可以通过Maven的功能在社区版的IDEA去创建Web项目,下次实践看看

max佩恩
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部