简单来说,对象内存分配主要是在堆中分配。但是分配的规则并不是固定的,取决于使用的收集器组合以及JVM内存相关参数的设定

以下介绍几条基本规则(使用的ParNew+Serial Old收集器组合):

一,对象优先在新生代Eden区分配

//-XX:+UseParNewGC -Xms20m -Xmx20m -Xmn10m -XX:+PrintHeapAtGC -XX:+PrintGCDetails
public class test {
static int mb = 1024*1024; public static void main(String[] args) {
byte[] b1 = new byte[2*mb];
System.out.println("b1 over");
byte[] b2 = new byte[2*mb];
System.out.println("b2 over");
byte[] b3 = new byte[2*mb];
System.out.println("b3 over");//GC
byte[] b4 = new byte[4*mb];
System.out.println("b4 over");
}
}

堆内存大小为20M,不可自动扩展,新生代内存大小为10M,根据默认值,Eden区:Survivor区为8:1,Eden区大小应为:10M*8/10=8129KB,Survivor区大小应为1024KB,新生代总可用内存应为9216KB

当b3分配完成后,新生代将使用6M内存(6144KB,b1+b2+b3),同时申请b4的4M=4096KB内存,此时新生代的可用内存为9216-6144=3072KB,不足以分配b4的空间,则触发一次Minor GC回收新生代内存空间,由于b1、b2以及b3都为存活状态,并且剩余的一个Survivor区无法装下b1、b2和b3,则新生代会租借老年代的区域,并将b1、b2和b3移动至租借区域,然后新生代完成Minor GC。由于此时新生代已经没有对象存放其中,剩余大量内存,则b4将在新生代中分配

b1 over
b2 over
b3 over
{Heap before GC invocations=0 (full 0):
par new generation total 9216K, used 6487K [0x03b30000, 0x04530000, 0x04530000)//b1+b2+b3,占6M
eden space 8192K, 79% used [0x03b30000, 0x04185f60, 0x04330000)
from space 1024K, 0% used [0x04330000, 0x04330000, 0x04430000)
to space 1024K, 0% used [0x04430000, 0x04430000, 0x04530000)
tenured generation total 10240K, used 0K [0x04530000, 0x04f30000, 0x04f30000)//老年代为空
the space 10240K, 0% used [0x04530000, 0x04530000, 0x04530200, 0x04f30000)
compacting perm gen total 12288K, used 2105K [0x04f30000, 0x05b30000, 0x08f30000)
the space 12288K, 17% used [0x04f30000, 0x0513e478, 0x0513e600, 0x05b30000)
No shared spaces configured.
[GC [ParNew: 6487K->150K(9216K), 0.0092952 secs] 6487K->6294K(19456K), 0.0093314 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]//对象仍处于存活状态,新生代无足够的空间完成Minor GC,只能租借老年代的空间,将b1、b2和b3移动至老年代
Heap after GC invocations=1 (full 0):
par new generation total 9216K, used 150K [0x03b30000, 0x04530000, 0x04530000)//新生代几乎被清空
eden space 8192K, 0% used [0x03b30000, 0x03b30000, 0x04330000)
from space 1024K, 14% used [0x04430000, 0x04455a10, 0x04530000)
to space 1024K, 0% used [0x04330000, 0x04330000, 0x04430000)
tenured generation total 10240K, used 6144K [0x04530000, 0x04f30000, 0x04f30000)//b1+b2+b3
the space 10240K, 60% used [0x04530000, 0x04b30030, 0x04b30200, 0x04f30000)
compacting perm gen total 12288K, used 2105K [0x04f30000, 0x05b30000, 0x08f30000)
the space 12288K, 17% used [0x04f30000, 0x0513e478, 0x0513e600, 0x05b30000)
No shared spaces configured.
}
b4 over
Heap
par new generation total 9216K, used 4410K [0x03b30000, 0x04530000, 0x04530000)//b4
eden space 8192K, 54% used [0x03b30000, 0x03f82008, 0x04330000)
from space 1024K, 14% used [0x04430000, 0x04455a10, 0x04530000)
to space 1024K, 0% used [0x04330000, 0x04330000, 0x04430000)
tenured generation total 10240K, used 6144K [0x04530000, 0x04f30000, 0x04f30000)//b1+b2+b3
the space 10240K, 60% used [0x04530000, 0x04b30030, 0x04b30200, 0x04f30000)
compacting perm gen total 12288K, used 2116K [0x04f30000, 0x05b30000, 0x08f30000)
the space 12288K, 17% used [0x04f30000, 0x051413c8, 0x05141400, 0x05b30000)
No shared spaces configured.

二,大对象直接进入老年代

为了避免内存回收时大对象在Eden区和2个Survivor区之间的拷贝(ParNew收集器使用复制算法),同时为了避免为了提供足够的内存空间而提前触发的GC,虚拟机提供了-XX:PretenureSizeThreshold(该设置只对Serial和ParNew收集器生效)参数,大于该参数设置值的对象将直接在老年代分配

//-XX:+UseParNewGC -Xms20m -Xmx20m -Xmn10m -XX:+PrintHeapAtGC -XX:+PrintGCDetails
//-XX:PretenureSizeThreshold=2097152
public class test {
static int mb = 1024*1024; public static void main(String[] args) {
byte[] b1 = new byte[3*mb];
System.out.println("b1 over");
}
}

由于设置超过2M(2*1024*1024=2097152B)的对象直接在老年代分配,故b1将分配在老年代上

b1 over
Heap
par new generation total 9216K, used 507K [0x03b50000, 0x04550000, 0x04550000)//新生代几乎为空
eden space 8192K, 6% used [0x03b50000, 0x03bcef00, 0x04350000)
from space 1024K, 0% used [0x04350000, 0x04350000, 0x04450000)
to space 1024K, 0% used [0x04450000, 0x04450000, 0x04550000)
tenured generation total 10240K, used 3072K [0x04550000, 0x04f50000, 0x04f50000)//老年代使用了3*1024K内存
the space 10240K, 30% used [0x04550000, 0x04850010, 0x04850200, 0x04f50000)
compacting perm gen total 12288K, used 2110K [0x04f50000, 0x05b50000, 0x08f50000)
the space 12288K, 17% used [0x04f50000, 0x0515f8c8, 0x0515fa00, 0x05b50000)
No shared spaces configured.

三,长期存活对象将进入老年代

由于虚拟机垃圾收集是基于“分代算法”的,故虚拟机必须能够识别哪些对象存放在新生代,哪些对象应该存放在老年代

虚拟机设计了一个对象年龄计数器,如果对象在Eden区出生并且经过第一次Minor GC后依然存活,并且可以被Survivor区容纳,就会被复制至Survivor区并将对象年龄设置为1。以后对象每熬过一次Minor GC,对象年龄便+1。当对象年龄超过对象晋升老年代的年龄阀值(该阀值默认为15)时,便会晋升至老年代,何时晋升,我们接下来研究

虚拟机提供了-XX:MaxTenuringThreshold参数设置晋升阀值

//-XX:+UseParNewGC -Xms20m -Xmx20m -Xmn10m -XX:+PrintHeapAtGC -XX:+PrintGCDetails
//-XX:MaxTenuringThreshold=1
public class test {
static int mb = 1024*1024; public static void main(String[] args) {
System.out.println("step 1");
byte[] b1 = new byte[1*mb/4];
System.out.println("step 2");
byte[] b2 = new byte[4*mb];
System.out.println("step 3");
byte[] b3 = new byte[4*mb];//GC
System.out.println("step 4");
b3 = null;
System.out.println("step 5");
b3 = new byte[4*mb];//GC
}
}

b1、b2正常分配。在step3,新生代将没有足够的内存分配b3所需的4M空间,故引发一次Minor GC。b1只有256KB,可以放置在Survivor区中,故复制b1到Survivor区中,b2为4M,无法放置到Survivor区中,故租借老年代4M内存放置b2,回收新生代内存空间,b1经历了一次Minor GC后依然存活,故年龄变为1。

在step4,分配给b3对象的内存空间依然被占用,只是将b3对象的引用置为空,由于不涉及到内存分配,故而不涉及到GC,因此对象的年龄也不会发生变化

在step5,重新给b3对象分配4M空间,由于新生代没有足够内存,故引发Minor GC,step3分配给b3的4M内存空间由于不再与存活对象相关联,将被回收,同时,由于b1的年龄到达对象晋升老年代的年龄设置,b1将被移动至老年代

step 1
step 2
step 3
{Heap before GC invocations=0 (full 0):
par new generation total 9216K, used 4695K [0x03b80000, 0x04580000, 0x04580000)//b1+b2
eden space 8192K, 57% used [0x03b80000, 0x04015f50, 0x04380000)
from space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 0K [0x04580000, 0x04f80000, 0x04f80000)//此时老年代为空
the space 10240K, 0% used [0x04580000, 0x04580000, 0x04580200, 0x04f80000)
compacting perm gen total 12288K, used 2105K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518e450, 0x0518e600, 0x05b80000)
No shared spaces configured.
[GC [ParNew: 4695K->409K(9216K), 0.0049519 secs] 4695K->4505K(19456K), 0.0049944 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=1 (full 0):
par new generation total 9216K, used 409K [0x03b80000, 0x04580000, 0x04580000)//b1
eden space 8192K, 0% used [0x03b80000, 0x03b80000, 0x04380000)
from space 1024K, 39% used [0x04480000, 0x044e6610, 0x04580000)
to space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
tenured generation total 10240K, used 4096K [0x04580000, 0x04f80000, 0x04f80000)//b2
the space 10240K, 40% used [0x04580000, 0x04980010, 0x04980200, 0x04f80000)
compacting perm gen total 12288K, used 2105K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518e450, 0x0518e600, 0x05b80000)
No shared spaces configured.
}
step 4
step 5
{Heap before GC invocations=1 (full 0):
par new generation total 9216K, used 4669K [0x03b80000, 0x04580000, 0x04580000)//b1+b3(step3)
eden space 8192K, 52% used [0x03b80000, 0x03fa9098, 0x04380000)
from space 1024K, 39% used [0x04480000, 0x044e6610, 0x04580000)
to space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
tenured generation total 10240K, used 4096K [0x04580000, 0x04f80000, 0x04f80000)//b2
the space 10240K, 40% used [0x04580000, 0x04980010, 0x04980200, 0x04f80000)
compacting perm gen total 12288K, used 2111K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518fe08, 0x05190000, 0x05b80000)
No shared spaces configured.
[GC [ParNew: 4669K->43K(9216K), 0.0008256 secs] 8765K->4548K(19456K), 0.0008701 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=2 (full 0):
par new generation total 9216K, used 43K [0x03b80000, 0x04580000, 0x04580000)//step3分配的b3对象空间被回收
eden space 8192K, 0% used [0x03b80000, 0x03b80000, 0x04380000)
from space 1024K, 4% used [0x04380000, 0x0438ad90, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 4505K [0x04580000, 0x04f80000, 0x04f80000)//b1+b2
the space 10240K, 43% used [0x04580000, 0x049e6590, 0x049e6600, 0x04f80000)
compacting perm gen total 12288K, used 2111K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518fe08, 0x05190000, 0x05b80000)
No shared spaces configured.
}
Heap
par new generation total 9216K, used 4303K [0x03b80000, 0x04580000, 0x04580000)//b3(step5)
eden space 8192K, 52% used [0x03b80000, 0x03fa8fe0, 0x04380000)
from space 1024K, 4% used [0x04380000, 0x0438ad90, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 4505K [0x04580000, 0x04f80000, 0x04f80000)//b1+b2
the space 10240K, 43% used [0x04580000, 0x049e6590, 0x049e6600, 0x04f80000)
compacting perm gen total 12288K, used 2116K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x051913c8, 0x05191400, 0x05b80000)
No shared spaces configured.

如果修改MaxTenuringThreshold的值为2,从打印日志中可以发现,最终老年代的内存使用量为4096KB=4M,也就是说b1没有晋升至老年代

上面是Minor GC的运行状况,如果是Full GC呢:

//-XX:+UseParNewGC -Xms20m -Xmx20m -Xmn10m -XX:+PrintHeapAtGC -XX:+PrintGCDetails
//-XX:MaxTenuringThreshold=1
public class test {
static int mb = 1024*1024; public static void main(String[] args) {
byte[] b1 = new byte[1*mb/4];
System.gc();
}
}

这里我们使用的是Full GC,也就是老年代的GC。

Full GC通常至少伴随着一次Minor GC(并非绝对),看下面日志,这里的Minor GC应该至少发生了2次,一次Minor GC是不会把b1移动至老年代的

{Heap before GC invocations=0 (full 0):
par new generation total 9216K, used 599K [0x03b80000, 0x04580000, 0x04580000)//b1
eden space 8192K, 7% used [0x03b80000, 0x03c15f40, 0x04380000)
from space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 0K [0x04580000, 0x04f80000, 0x04f80000)//老年代为空
the space 10240K, 0% used [0x04580000, 0x04580000, 0x04580200, 0x04f80000)
compacting perm gen total 12288K, used 2104K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518e278, 0x0518e400, 0x05b80000)
No shared spaces configured.
[Full GC (System) [Tenured: 0K->404K(10240K), 0.0069434 secs] 599K->404K(19456K), [Perm : 2104K->2104K(12288K)], 0.0069992 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap after GC invocations=1 (full 1):
par new generation total 9216K, used 0K [0x03b80000, 0x04580000, 0x04580000)//新生代为空
eden space 8192K, 0% used [0x03b80000, 0x03b80000, 0x04380000)
from space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 404K [0x04580000, 0x04f80000, 0x04f80000)//b1
the space 10240K, 3% used [0x04580000, 0x045e5130, 0x045e5200, 0x04f80000)
compacting perm gen total 12288K, used 2104K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x0518e278, 0x0518e400, 0x05b80000)
No shared spaces configured.
}
Heap
par new generation total 9216K, used 327K [0x03b80000, 0x04580000, 0x04580000)
eden space 8192K, 4% used [0x03b80000, 0x03bd1f98, 0x04380000)
from space 1024K, 0% used [0x04380000, 0x04380000, 0x04480000)
to space 1024K, 0% used [0x04480000, 0x04480000, 0x04580000)
tenured generation total 10240K, used 404K [0x04580000, 0x04f80000, 0x04f80000)
the space 10240K, 3% used [0x04580000, 0x045e5130, 0x045e5200, 0x04f80000)
compacting perm gen total 12288K, used 2116K [0x04f80000, 0x05b80000, 0x08f80000)
the space 12288K, 17% used [0x04f80000, 0x05191190, 0x05191200, 0x05b80000)
No shared spaces configured.

四:动态对象年龄判定

为了使内存分配更加灵活,虚拟机并不要求对象年龄达到MaxTenuringThreshold才晋升老年代

如果Survivor区中相同年龄所有对象大小的总和大于Survivor区空间的一半,年龄大于或等于该年龄的对象在Minor GC时将复制至老年代

//-XX:+UseParNewGC -Xms20m -Xmx20m -Xmn10m  -XX:MaxTenuringThreshold=10
//-XX:+PrintTenuringDistribution
public class Test {
static int mb = 1024*1024; public static void main(String[] args) {
System.out.println("step 1");
byte[] b1 = new byte[1*mb/4];
byte[] b3 = new byte[4*mb];
byte[] b4 = new byte[4*mb];//GC
System.out.println("step 2");
byte[] b2 = new byte[1*mb/4];//可以尝试1*mb/2,然后观察日志
b4 = null;
System.out.println("step 3");
b4 = new byte[4*mb];//GC
System.out.println("step 4");
b4 = null;
b4 = new byte[4*mb];//GC
}
}

先来介绍一个设置-XX:+PrintTenuringDistribution,这个参数很有意思,会在Minor GC时打印Survivor区内存容量的一半,晋升老年代年龄阀值,Survivor区中的对象大小以及对象年龄

根据启动参数的设置,Survivor大小的一半是524288B,也就是512KB。第一次GC后,b1依然存活,故年龄变为1。第二次GC后,b1和b2依然存活,故b1的年龄变为2,b2的年龄为1。b1+b2的大小加起来超过了Survivor区容量的一半,此时会修改Survivor区晋升老年代年龄阀值为2(如果移动年龄为2的对象可以使Survivor去的内存使用降至512KB以内,则只移动年龄为2的对象,否则将会同时移动年龄为1的对象)。第三次GC时,将年龄等于晋升阀值的对象移动至老年代,执行GC,GC结束后,b1依然在Survivor区(当然可能从Survivor
from区拷贝至了Survivor to区),此时b1的年龄变为2。这时Survivor区的使用内存没有达到512M,修改Survivor区晋升老年代年龄阀值为参数设置的10。

step 1

Desired survivor size 524288 bytes, new threshold 10 (max 10)
- age 1: 412800 bytes, 412800 total
step 2
step 3 Desired survivor size 524288 bytes, new threshold 2 (max 10)
- age 1: 262160 bytes, 262160 total
- age 2: 412800 bytes, 674960 total
step 4 Desired survivor size 524288 bytes, new threshold 10 (max 10)
- age 1: 136 bytes, 136 total
- age 2: 262160 bytes, 262296 total

最后,为什么在第三次GC后,Survivor区还存在一个大小为136B,年龄为1的被使用内存空间?

我猜测,虽然Minor GC时Survivor区没有足够的空间完成GC时会租借老年代的内存,但是在Survivor区依然保存了一个指向老年代租借内存起始地址的引用

五:空间分配担保

这个前面已经出现过多次了,由于新生代使用复制算法,当Minor GC时如果存活对象过多,无法完全放入Survivor区,就会向老年代借用内存存放对象,以完成Minor GC

在触发Minor GC时,虚拟机会先检测之前GC时租借的老年代内存的平均大小是否大于老年代的剩余内存,如果大于,则将Minor GC变为一次Full GC,如果小于,则查看虚拟机是否允许担保失败(-XX:+/-HandlePromotionFailure。从jdk6.0开始,允许担保失败已变为HotSpot虚拟机所有收集器默认设置,虚拟机将不再识别该参数设置,详见JDK-6990095 : Deprecate and eliminate -XX:-HandlePromotionFailure),如果允许担保失败,则只执行一次Minor
GC,否则也要将Minor GC变为一次Full GC(直到GC结束时才能确定到底有多少对象需要被移动至老年代,所以在GC前,只能使用粗略的平均值进行判断)

JVM笔记4:Java内存分配策略的更多相关文章

  1. 深入理解JVM(5)——垃圾收集和内存分配策略

    1.垃圾收集对象 垃圾收集主要是针对堆和方法区进行. 程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收. 哪 ...

  2. [jvm]垃圾回收与内存分配策略

    一.垃圾回收算法 概述 JVM中,当创建的对象不再被使用的时候,此时我们认为他是无用的“垃圾”:在现代主流的商用jvm中,都是通过可达性分析来判断对象是否存活的.这个算法的基本思想是通过一系列“GCR ...

  3. JVM学习总结四——内存分配策略

    之前几篇我们介绍了jvm的内存模型以及垃圾回收机制,而本篇我们将介绍几个JVM中对象在分配内存是应该遵循的策略.毕竟,想要去优化程序,不仅要考虑垃圾回收的过程,还要从对象内存分配的角度减少gc的代价. ...

  4. Java 内存分配策略

    内存有分配,就有回收,Java 的 GC 算法在前一篇文章中已经介绍过了,这篇文章着重介绍 Java 的内存分配策略. 从大方向讲,除去 JIT ,对象的内存分配就是在堆上分配,对象主要分配在新生代的 ...

  5. JVM系列四(内存分配策略).

    一.概要 前面的文章介绍了对象的创建过程,其中第三步 -- 分配内存,只是简单的介绍了分配的方式 -- 指针碰撞.空闲列表,其实内存在堆上分配还大有文章嘞. 对象的内存分配,往大方向上讲,就是在堆上分 ...

  6. 《深入理解java虚拟机》读书笔记——垃圾收集与内存分配策略

    可回收判定两种算法 引用计数法(Reference Counting):引用为0时可回收. 可达性分析法(Reachability Analysis): 从GCRoots对象到这个对象不可达. GCR ...

  7. jvm垃圾收集器与内存分配策略

    一.垃圾回收 1.对象是否已经变为垃圾 1.1.引用计数法:给对象添加一个引用计数器,每当有地方引用它时,计数器就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用的. 这 ...

  8. 深入理解JVM - 垃圾收集器与内存分配策略 - 第三章

    引用计数算法——判断对象是否存活的算法 很多教科书判断对象是否存活的算法是这样的:给对象添加一个引用计数器,每当一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象 ...

  9. JVM垃圾收集器与内存分配策略(一)

    在前面的Java自动内存管理机制(上)和Java自动内存管理机制(下)中介绍了关于JVM的一些基础知识,包括运行时数据区域划分和一些简单的参数配置,而其中也谈到了GC,但是没有深入了解,所以这里开始简 ...

随机推荐

  1. MapReduce架构设计

    MapReduce采用Master/Slave的架构,其架构图如下: 它主要有以下4个部分组成: 1)Client 2)JobTracker JobTracke负责资源监控和作业调度.JobTrack ...

  2. bzoj2286

    很明显,20%=mincut 40%=每次暴力树形dp那么正解是什么呢?不难发现∑ki<=500000,也就是每次询问的复杂度都要跟k有关从树形dp工作的角度来看,确实有很多点我们根本就没必要访 ...

  3. c#浅谈反射内存的处理

    这段时间由于公司的项目的要求,我利用c#的反射的机制做了一个客户端框架.客户端里的所有的模块都是以一定形式进行提供,例如:FORM,UserControl. 在做的过程中很简单与愉快.具体的过程如下: ...

  4. windows下的BT服务器搭建方案

    BT下载可以说是目前网络上最流行的P2P文件共享方式了.BT服务器相比于FTP服务器有许多优势,其中最重要的一点就是BT服务器不受服务器带宽和连接到服务器用户数量的限制,能够提供高速的下载服务.今天, ...

  5. DNS----域名解析系统

    DNS就是域名解析系统,它可以将IP转换成域名,也可以将域名转换成IP 1. 安装DNS服务       开始—〉设置—〉控制面板—〉添加/删除程序—〉添加/删除Windows组件—〉“网络服务”—〉 ...

  6. GIS数据格式:Shapefile

    转自:http://lab.osgeo.cn/2449.html Shapefile是ESRI提出的数据格式,随着ArcView GIS 3.x发布,属于简单要素类.Shapefile由于其数据结构简 ...

  7. MongoDB之三(高级操作 聚合、游标)

    一: 聚合 常见的聚合操作跟sql server一样,有:count,distinct,group,mapReduce. <1> count count是最简单,最容易,也是最常用的聚合工 ...

  8. lightoj 1002

    最短路的变形,使用spfa做. #include<set> #include<map> #include<list> #include<stack> # ...

  9. ubuntu下arm-linux-gcc安装

    我下载的地址随便找的,4.4.3版本的,地址:http://www.cr173.com/soft/42654.html#address 1.我放在了/work/tools/ 2.sudo tar  x ...

  10. Clean Code – Chapter 5 Formatting

    The Purpose of Formatting Code formatting is about communication, and communication is the professio ...