1. 核心概念

1.1 堆区的特点

堆可以是物理上不连续的内存空间,但是在存储逻辑上是连续的。如果问你是否所有对象都分配到堆上,你需要回答出来一个逃逸分析和栈上分配的观点,后面有提到。

一个JVM实例只存在一个堆空间,并且所有的线程共享堆内存。

但是可以在堆内存划分线程私有的缓冲区(TLAB),下文会介绍

堆区的大小是可以调节的,但是Java堆区在JVM启动时就被创建,其大小也就确定了。并且是JVM中管理的最大的一块内存空间。在堆中创建的对象要等到GC的发生才能被移除。

2. 堆内部结构

2.1 堆内存细分

经研究表明70%-99%的对象属于临时对象,为了提高GC的性能,Hotspot虚拟机又将堆区进行了进一步划分。

Java JDK7及以前逻辑上被分为三部分:新生区、养老区、永久区

新生区被分为伊甸园区和幸存者0和幸存者1区

Java JDK8及以后被分为了:新生区、养老区元空间

在jdk1.8废弃了永久代,使用元空间(MetaSpace)取而代之,元空间存储的对象与永久代相同,区别是:元空间并不在jvm中,使用的是本地内存。其余和JDK1.7类似。

为什么要移除永久代呢?为融合HotSpot JVM与JRockit VM(新JVM技术)而做出的改变,因为JRockit没有永久代。

2.2 内部结构

3. 堆内存大小和OOM

通过两个JVM参数可以设置堆的初始内存和最大内存:

  • -Xms:设置堆的初始内存

  • -Xmx:设置堆的最大内存

默认情况下

  • 初始内存大小:物理电脑内存大小 / 64
  • 最大内存大小:物理电脑内存大小 / 4

一旦堆区中的内存大小超过“-Xmx"所指定的最大内存时,将会抛出OutOfMemoryError异常。

通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在Java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。

4. 年轻代和老年代

JVM中的Java对象可以被划分为两类:

一类是生命周期较短的对象,该类对象的创建和消亡都非常迅速;

另一类就是生命周期很长,在某些极端情况下还能和JVM的生命周期保持一致。

为什么要进行分代?

精炼解释:

将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。

详细解释:

为此,堆空间才被分为新生代和老年代,并将生命周期很短的对象放在新生代,将生命周期很长的对象放在老年代,因为在每次GC时,垃圾回收器都会去判断当前对象是否可以被回收,而这些生命周期很长的对象每次都被垃圾回收器扫描,但每次都不回收,故而可以将这些对象放在老年代,并减少对老年代的GC次数,从而将GC的重心放在新生代上。

通过这两个区域对象的生命周期不同,也可以设置不同的垃圾回收算法,比如对新生代中的对象采用复制算法,因为该区域的对象生命周期短,消亡快,所以当发生GC时并不会存在太多存活的对象,而对老年代则采用标记-清除算法,关于垃圾回收的具体内容在后面还会重点介绍。

为什么要将幸存者区分为两个部分?

主要为了解决碎片化,因为回收一部分对象后,剩余对象占用的内存不连续,也就是碎片化,过于严重的话,当前连续的内存不够新对象存放就会触发GC,这样会提高GC的次数,降低性能,当S0 GC后存活对象转移到S1后存活对象占用的就是连续的内存。

比如一开始只有Eden区和From中有对象,To中是空的。此时进行一次GC操作,From区中对象的年龄就会+1,我们知道Eden区中所有存活的对象会被复制到To区,From区中还能存活的对象会有两个去处。

若对象年龄达到之前设置好的年龄阈值(默认年龄为15岁,可以自行设置参数‐XX:+MaxTenuringThreshold),此时对象会被移动到Old区, 如果Eden区和From区 没有达到阈值的对象会被复制到To区。

此时Eden区和From区已经被清空(被GC的对象肯定没了,没有被GC的对象都有了各自的去处)。

这时候From和To交换角色,之前的From变成了To,之前的To变成了From。也就是说无论如何都要保证名为To的Survivor区域是空的。

4.1 组成

Java堆区可以分为 年轻代和老年代。

年轻代可以分为Eden区,Survivor0区和Survivor1区(有时候也叫from区和to区,哪个是空的哪个是to区,同一时候只有一个幸存者区是有数据的)

几乎所有的Java对象都是在Eden区被new出来的,有的大对象在该区存不下可直接进入老年代。绝大部分的Java对象都销毁在新生代了(IBM公司的专门研究表明,新生代80%的对象都是“朝生夕死”的)

4.2 参数和占比

配置新生代与老年代在堆结构的占比。

  • 默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3

伊甸园区和幸存者区占比

-XX:SurvivorRatio=6

Eden区和两个Survivor区的内存空间占比默认为:8:1:1 ,也可以通过虚拟机参数修改。

几乎所有的Java对象都是在Eden区被创建出来的,如果创建的对象所需内存非常大,以至于Eden区根本放不下,那么该对象就会直接在老年代创建。

5. 对象分配过程

一般情况下,新创建的对象都会被分配到Eden区(朝生夕死),一些特殊的大的对象会直接分配到Old区。

  1. 创建的对象首先存放在Eden区

  2. 当Eden区的空间满了以后,此时创建对象便会触发GC(Minor GC),将Eden区中存活的对象放入幸存区,然后清除Eden区

  3. 当触发Eden区的GC时,会将Eden区中还存活的对象放入幸存区S0

  4. 当Eden区满了再次触发GC时,会将Eden区中存活的对象和幸存者S0中仍然存活的对象放入幸存区S1

  5. 若再一次触发Eden区的GC,则将存活的对象又重新放回幸存区S0,依次循环

  6. 存活的对象被放入幸存区一次,年龄就会加1,当对象的年龄到达15岁时,该对象就会被晋升到老年代

注意在这个过程中,只有Eden区满时才会触发GC,此时垃圾回收器会对Eden区和Survivor区进行清理,Survivor区满并不会触发GC,而且GC完成后,Eden区是一个空的状态。当要创建的对象内存超过Eden区空间时,该对象会被直接晋升到老年代,若是老年代仍然放不下,则触发一次在老年代的GC(Full GC),如果GC完成后还是放不下,则出现 OutOfMemoryError: Java heap space 错误。

过程图:

流程图:

  • 新对象申请内存,如果Eden放的下,则直接存入Eden;如果存不下则进行YGC

  • YGC之后如果能存下则放入Eden,如果还存不下(为超大对象),则尝试存入Old区;

  • 如果Old区可以存放,则存入;如果不能存入,则进行Full GC

  • Full GC之后如果可以存入Old区,则存入;如果内存空间还不够,则OOM

  • 图右侧为YGC的流程图:当YGC之后未销毁的对象放入幸存者区,此时如果幸存者区的空间可以装下该对象,则存入幸存者区,否则,直接存入老年代;

  • 当在幸存者区的对象超过阈值时,可以晋升为老年代,未达到阈值的依旧在幸存者区复制交换。

针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to

关于垃圾回收:频繁在新生区收集,很少在老年代收集,几乎不再永久代和元空间进行收集

6. Minor GC,MajorGC、Full GC

JVM在进行GC的时候,并不是每次都对上面的三个内存区域一起回收的,大部分的时候回收的都是新生代的东西。按照回收区域可以分成两个部分,分别为部分收集和整堆收集:

部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:

新生代收集(Minor GC / Young GC):只是新生代的垃圾收集。只有当新生代中的Eden区满时才会触发,Survivor区满是不会触发GC的

老年代收集(Major GC / Old GC):只是老年代的圾收集。目前,只有CMSGC会有单独收集老年代的行为。(注意,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。)

混合收集(MixedGC):收集整个新生代以及部分老年代的垃圾收集。目前,只有G1 GC会有这种行为

整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

6.1 年轻代GC(Minor GC)触发机制

当年轻代的伊甸园区的空间不足时,就会触发Minor GC,每次Minor GC会清楚年轻代的内存。

因为Java对象大多都具备朝生夕灭的特性.,所以Minor GC非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。

只有当新生代中的Eden区满时才会触发,Survivor区满是不会触发GC的

Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

JDK6 Update 24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行FullGC。

细心的同学应该发现了,对于两块幸存区,有时候叫它S0和S1,而有时候又叫他们From和To,这是什么情况呢?

事实上,S0就是From区,S1就是To区,但由于复制-清除算法的过程,它会将第一次Minor GC后存活的对象放入From区,此时To区是空的;当第二次Minor GC时,垃圾回收器会扫描Eden区和From区,并将还存活的对象放入To区,然后清空Eden和From区,此时From区会和To区做一个交换,这样空的From区就会作为下一次GC的To区继续放置存活的对象,即:复制之后有交换,谁空谁就做To区 。 

6.2 老年代GC(Major GC / Full GC)触发机制

也就是在老年代空间不足时,会先尝试触发Minor Gc。如果之后空间还不足,则触发Major GC

Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长

如果Major GC后,内存还不足,就报OOM了

6.3 Full GC触发机制(后面细讲)

触发Full GC执行的情况有如下五种:

  • 调用System.gc()时,系统建议执行Full GC,但是不必然执行
  • 老年代空间不足
  • 方法区空间不足
  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
  • 由Eden区、survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

说明:Full GC 是开发或调优中尽量要避免的。这样暂时时间会短一些

7. 内存分配策略

当对象第一次被创建会到伊甸园区,经历Minor GC以后如果仍然存活并且幸存者区有足够的空间容纳的话,将对象移到幸存者区并且年龄设为1。对象在survivor区中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,其实每个JVM、每个GC都有所不同)时,就会被晋升到老年代。如果幸存者区放不下,则直接放到老年代。(晋升老年代的年龄阀值,可以通过选项-XX:MaxTenuringThreshold来设置)

针对不同年龄段的对象分配原则如下所示:

  • 优先分配到Eden
  • 大对象直接分配到老年代(尽量避免程序中出现过多的大对象)
  • 长期存活的对象分配到老年代
  • 动态对象年龄判断:如果survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
  • 空间分配担保: -XX:HandlePromotionFailure

7.1 数值变小原理

我们设置好新生代的大小以后,在通过命令行去查看的时候,所返回的数值比我们设置的要小。

//代码样例,设置参数:-Xms600m,-Xmx600m
public class HeapSpaceInitial {public static void main(String[] args) {//返回Java虚拟机中的堆内存总量long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;//返回Java虚拟机试图使用的最大堆内存量long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;System.out.println("-Xms : " + initialMemory + "M");System.out.println("-Xmx : " + maxMemory + "M");try {Thread.sleep(1000000);} catch (InterruptedException e) {e.printStackTrace();}}
}
//执行结果
//-Xms : 575M
//-Xmx : 575M

这是因为这是因为在堆内存存取数据时,新生代里边只有伊甸园和幸存者1区或者是幸存者2区存储对象,所以会少一个幸存者区的内存空间。

8. 对象创建所在区域

一般情况下,新创建的对象都会被分配到Eden区(朝生夕死),一些特殊的大的对象会直接分配到Old区。

比如有对象A,B,C等创建在Eden区,但是Eden区的内存空间肯定有限,比如有100M,假如已经使用了100M或者达到一个设定的临界值,这时候就需要对Eden内存空间进行清理,即垃圾收集(Garbage Collect),这样的GC我们称之为Minor GCMinor GC指得是Young区的GC

经过GC之后,有些对象就会被清理掉,有些对象可能还存活着,对于存活着的对象需要将其复制到Survivor区,然后再清空Eden区中的这些对象。

TLAB的全称是 Thread Local Allocation Buffer,JVM默认给每个线程开辟一个 buffer 区域,用来加速对象分配。这个 buffer 就放在 Eden 区中。

这个道理和 Java 语言中的ThreadLocal类似,避免了对公共区的操作,以及一些锁竞争。

8.1 为什么要有TLAB?

由于堆区是整个JVM只存在一份,多个线程共享的区域。并且对象实例的创建是非常频繁的,因此在堆区中划分内存空间是线程不安全的。要想保证线程安全则就需要给线程加锁,这样会严重拖累运行速度。TLAB就是JVM为每个线程分配的一块私有的缓存区域,线程对这缓存的操作相互独立。

8.2 什么是TLAB

在伊甸园区内继续划分,将其分为连续的一块块的存储区域。该缓存区域是线程私有的。

多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略。

8.3 TLAB详细说明

尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选。

在程序中,开发人员可以通过选项“-XX:UseTLAB”设置是否开启TLAB空间。

默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,当然我们可以通过选项 “-XX:TLABWasteTargetPercent” 设置TLAB空间所占用Eden空间的百分比大小。

一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存。

堆空间的参数设置

-XX:+PrintFlagsInitial:查看所有的参数默认值-XX:+PrintFlagsFinal:查看所有的参数最终值-Xms:设置初始堆内存-Xmx:设置最大堆内存-Xmn:设置新生代的内存-XX:NewRatio:配置新生代与老年代在堆结构的占比-XX:SurvivorRatio:配置新生代中Eden区与Survivor区的占比-XX:MaxTenuringThreshold:设置新生代垃圾的最大年龄-XX:+PrintGCDetails:输出GC的详细日志

9. 逃逸分析

9.1 概述

着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

也就是说Java的内存分配普遍还是在堆上进行的,但是存在一种特殊情况。 如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。

当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。

当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。

没有发生逃逸的对象,则可以分配到栈上,随着方法执行的结束,栈空间就被移除。

在JDK 6u23 版本之后,HotSpot中默认就已经开启了逃逸分析

如果使用的是较早的版本,开发人员则可以通过:

  • 选项“-XX:+DoEscapeAnalysis"显式开启逃逸分析
  • 通过选项“-XX:+PrintEscapeAnalysis"查看逃逸分析的筛选结果

例1:

例2:

9.2 代码优化

经过逃逸分析,编译器可以对代码进行栈上分配,同步省略,分离对象或标量替换。

9.2.1 栈上分配

JIT编译器在编译期间根据逃逸分析的结果,发现如果一个对象并没有逃逸出方法的话,就可能被优化成栈上分配。分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。这样就无须进行垃圾回收了。

9.2.2 同步省略

线程同步的代价是相当高的,同步的后果是降低并发性和性能。

在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有,那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就叫同步省略,也叫锁消除。

public void f() {Object hellis = new Object();synchronized(hellis) {System.out.println(hellis);}
}
//优化后
public void f() {Object hellis = new Object();System.out.println(hellis);
}

9.2.3 标量替换

标量(scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量.相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。

在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。

public static void main(String args[]) {alloc();
}
private static void alloc() {Point point = new Point(1,2);System.out.println("point.x" + point.x + ";point.y" + point.y);
}
class Point {private int x;private int y;
}private static void alloc() {int x = 1;int y = 2;System.out.println("point.x = " + x + "; point.y=" + y);
}

9.3 逃逸分析总结

无法保证逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。虽然这项技术并不十分成熟,但是它也是即时编译器优化技术中一个十分重要的手段。

Oracle Hotspot JVM中并未这么做,这一点在逃逸分析相关的文档里已经说明,所以可以明确所有的对象实例都是创建在堆上

10. 堆区常见问题

10.1 请说一下Minor/Major/Full GC分别发送在哪个区域?

Minor GC:发生在年轻代的 GC
Major GC:发生在老年代的 GC。
Full GC:新生代+老年代,比如 Metaspace 区引起年轻代和老年代的回收。

10.2 为什么需要Survivor区?只有Eden不行吗?

如果没有Survivor,Eden区每进行一次Minor GC,并且没有年龄限制的话, 存活的对象就会被送到老年代。

这样一来,老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。

老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。

执行时间长有什么坏处?

频发的Full GC消耗的时间很长,会影响大型程序的执行和响应速度。

那就对老年代的空间进行增加或者较少呢?

假如增加老年代空间,更多存活对象才能填满老年代。虽然降低Full GC频率,但是随着老年代空间加大,一旦发生FullGC,执行所需要的时间更长。

假如减少老年代空间,虽然Full GC所需时间减少,但是老年代很快被存活对象填满,Full GC频率增加。

所以Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

10.3 为什么需要两个大小一样的Survivor区?

最大的好处就是解决了碎片化。也就是说为什么一个Survivor区不行?

第一部分中,我们知道了必须设置Survivor区。假设现在只有一个Survivor区,我们来模拟一下流程:

刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。

这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。

永远有一个Survivor space是空的,另一个非空的Survivor space无碎片。

10.3  新生代中Eden:S1:S2为什么是8:1:1?

新生代中的可用内存:复制算法用来担保的内存为9:1,所以只会造成 10% 的空间浪费。
可用内存中Eden:S1区为8:1
即新生代中Eden:S1:S2 = 8:1:1

这个比例,是由参数 -XX:SurvivorRatio 进行配置的(默认为 8)。

堆区(Heap)详细介绍相关推荐

  1. 返回局部变量或临时变量的地址_值传递和地址返回两者在堆区(Heap)应用的三种易错点...

    1.指针变量作为参数进行值传递给函数的形参,并在堆区(Heap)进行内存分配和赋值 程序源码: 1 结果: Segmentation fault (core dumped) 分析: 如上图,指针变量p ...

  2. Java虚拟机内存的堆区(heap),栈区(stack)和静态区(static/method)

    JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) 堆区: 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息.(class的目的是得到操作指 ...

  3. 堆(heap)系列_0x05:一张图剖析堆块分配和FreeLists的联系

    提示:前面几篇笔记基本都是侧重理论,下面用2个示例进行讲解:一个是堆块的分配和释放,另一个是FreeLists的作用(可以直接看下面的图,很容易理解,画了一个多小时-) 文章目录 示例1:堆块分配和释 ...

  4. C/C++ 内存分配方式,堆区,栈区,new/delete/malloc/free

    内存分配方式有三种: [1] 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量, static 变量. [2] 在栈上创建.在执行函数时,函数内局 ...

  5. final string 堆区 栈区 java_JVM内存初学 堆、栈、方法区

    转自: http://www.open-open.com/lib/view/open1432200119489.html 这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解J ...

  6. 11.JDK8内存模型、本地方法栈、虚拟机栈、栈帧结构(局部变量表、操作数栈、方法出口、虚拟机栈与本地方法栈的关系、寄存器、方法区、堆(Heap)、jvm中的常量池、Metaspace(元空间))

    11.JDK8内存模型 11.1.本地方法栈(Native Method Stacks) 11.2.虚拟机栈(Java Virtual Machine Stacks) 11.3.栈帧结构 11.3.1 ...

  7. java栈 类 堆_详细介绍Java中的堆和栈

    栈与堆都是Java用来在RAM中寄存数据的中央.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. Java的堆是一个运转时数据区,类的对象从中分配空间.这些对象经过new.newar ...

  8. stack vs heap:栈区分配内存快还是堆区分配内存快 ?

    作者 | 码农的荒岛求生 来源 | 码农的荒岛求生 有伙伴问到底是从栈上分配内存快还是从堆上分配内存快,这是个比较基础的问题,今天就来聊一聊. 栈区的内存申请与释放 毫无疑问,显然从栈上分配内存更快, ...

  9. 明日之后android和ios,明日之后安卓ios互通区有哪些?ios安卓混服区详细介绍

    原标题:明日之后安卓ios互通区有哪些?ios安卓混服区详细介绍 明日之后安卓ios是有互通区的,但是昨天安卓平台上线之后,很多安卓玩家找不到互通区服务器,所以今天小编给大家说说游戏中具体的互通服务器 ...

最新文章

  1. java 封装事务_Spring之路(36)–事务很重要吗?Spring为何要封装事务?Spring事务有陷阱吗?...
  2. 阿里取消“361”绩效考核,是打工人的福报吗?
  3. favicon自动获取_友情链接前面自动获取并添加favicon.ico小图标
  4. 初步了解超图两种3D客户端 - iClient3D for_WebGL 和 iClient3D for_Plugin
  5. golang中如何抓取panic让程序正常退出
  6. java对象工厂池_[转载]Java对象池技术的原理及其实现
  7. Linux学习笔记3
  8. Bootstrap3 栅格系统之列平移
  9. 小程序飞入购物车特效
  10. mdx格式的词典用什么软件打开_可能是目前PC端最好用的词典——Goldendict
  11. php 字符串循环替换字符串,php – 替换字符串中的重复字符串
  12. xrld读取excel 中合并单元格内容
  13. 手机浏览器播放mp3等音乐(chrome特殊)
  14. 鸿蒙IPC摄像机,【HiSpark IPC DIY Camera试用连载 】第二篇 视频的人脸检测
  15. 范围变更管控案例_项目范围管理案例之范围确认案例
  16. JAVA高并发多线程必须懂的50个问题
  17. 智能温度、电压监测系统
  18. VS2022的下载和使用
  19. 每日一记 - 3.8
  20. 单片机原理及接口技术期末复习

热门文章

  1. 苹果6plus性能测试软件,iPhone 6、iPhone6 Plus性能测试
  2. c语言远控,远控鼠标!C语言简单编程:整舍友必备+附送实例源码!
  3. 第19组 Beta(1/3)
  4. 玩手机惹怒丈夫 男子用菜刀砍死怀孕8个月妻子
  5. c语言未定义标识符fife,C语言编程处理数据
  6. PTA 广西科技大学 胡伟平老师出的题目
  7. html文字图片一起轮播卡片,简单的堆叠卡片样式jQuery轮播图插件
  8. Panda3D设置游戏背景颜色和节点颜色、透明度
  9. java 生成word表格
  10. 域策略(6)——统一映射共享文件夹