一、环境

JDK 垃圾收集器 是否启用TLAB 通用JVM参数(堆内存分配见下图)
1.6.0_65 Serial + Serial Old -Xms20m -Xmx20m -Xmn10m -XX:SurvivorRatio=8

二、说明

  • Minor GC

    • 发生在新生代,当Eden区域没有足够空间进行分配
    • Java对象大多具有短命的特性
    • Minor GC非常频繁,速度也比较
  • Major GC / Full GC
    • 发生在老年代
    • 出现Major GC,经常伴随至少一次Minor GC
    • SpeedOf (Minor GC) ≈ 10 * SpeedOf (Major GC)

三、示例

1. 对象优先分配在Eden区

1.1 说明

  • 新对象,优先考虑分配在Eden区域
  • 如果Eden区域没有足够的空间容纳新对象,进行GC
    • 如果老年代有足够的连续空间用来存储所有新生代对象(或历次晋升的平均大小) ⇒ Minor GC

      • 如果对象太大,以至于Survivor区域无法容纳,对象直接晋升到老年代
      • 否则使用复制算法,复制到Survivor区域
    • 否则 ⇒ 先进行一次Minor GC,若仍不满足上述条件,进行Full GC
      • Full GC后依然内存不足,

1.2 代码

  1. # 代码
  1. public class TestAllocation {
  2. private static final int _1MB = 1024 * 1024;
  3.  
  4. // JVM Args:
  5. // -XX:+PrintGCDetails
  6. // -XX:+UseSerialGC
  7. // -Xms20m -Xmx20m
  8. // -Xmn10m -XX:SurvivorRatio=8
  9. public static void main(String[] args) throws InterruptedException {
  10. System.out.println("===1. start full gc start===");
  11. System.gc();
  12. System.out.println("===1. start full gc end===\n");
  13.  
  14. System.out.println("===2. gc logs===");
  15. byte[] a1 = new byte[_1MB / 4];
  16. byte[] a2 = new byte[4 * _1MB];
  17. byte[] a3 = new byte[4 * _1MB];// 一次Minor GC
  18. byte[] a4 = new byte[4 * _1MB];// 一次Minor GC
  19. byte[] a5 = new byte[4 * _1MB];// 一次Minor GC + 一次Full GC ⇒ OOM
  20. }
  21. }
  1.  

1.3 运行结果

  1. # 仅保留关键信息
  1. ===1. start full gc start===
  2. [Full GC (System) [Tenured: 0K->426K(10240K)] 1974K->426K(19456K)
  3. ===1. start full gc end===
  4.  
  5. ===2. gc logs===
  6. [GC [DefNew: 4843K->256K(9216K)] 5270K->4778K(19456K)]
  7. [GC [DefNew: 4352K->256K(9216K)] 8874K->8874K(19456K)]
  8. [GC [DefNew: 4436K->4436K(9216K)][Tenured: 8618K->8618K(10240K)] 13055K->12970K(19456K)
  9. [Full GC [Tenured: 8618K->8549K(10240K)] 12970K->12902K(19456K)
  10.  
  11. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  12.  
  13. Heap
  14. def new generation total 9216K, used 4657K
  15. eden space 8192K, 56% used
  16. from space 1024K, 0% used
  17. to space 1024K, 0% used
  18. tenured generation total 10240K, used 8549K
  19. the space 10240K, 83% used
  1.  

1.4 运行示意图

1.5 运行过程解析

  1. System.gc() ⇒ Full GC

    • 新生代已分配内存:0K
    • 老年代已分配内存:426K
  2. byte[] a1 = new byte[_1MB / 4];
    • Eden空间充足,为a1分配256K内存
  3. byte[] a2 = new byte[4 * _1MB];
    • Eden空间充足,为a2分配4096K内存
  4. byte[] a3 = new byte[4 * _1MB];
    • Eden空间不足,不能为a3分配4096K内存,需要进行GC

      • 4.1 判定是否需要Full GC

        • 老年代未分配的连续空间:10240-427 = 9813K
        • 所有的新生代对象:sizeof(a1)+size(a2)=256+4096 = 4352K
        • 9813 ≫ 4352 ⇒ Minor GC
      • 4.2 进行Minor GC
        • 1024 > 256 ⇒ remainingSpaceOf(Survivor) > sizeof(a1) ⇒ a1复制到to(Survivor区)
        • 4096 > 1024 ⇒ sizeof(a2) > sizeof(Survivor) ⇒ a2晋升到Tenured区
      • 4.3 Minor GC之后,Eden空间充足,为a3分配4096K内存
        • 新生代 – 4352K

          • Eden:4096K(a3)
          • from(Survivor):256K(a1)
          • to(Survivor):0K
        • 老年代 – 4522K
          • Tenured:426K + 4096K(a2)
  5. byte[] a4 = new byte[4 * _1MB];
    • Eden空间不足,不能为a4分配4096K内存,需要进行GC

      • 5.1 判定是否需要Full GC

        • 老年代未分配的连续空间:10240-427-4096 = 5717K
        • 所有的新生代对象:sizeof(a1)+size(a2)=256+4096 = 4352K
        • 5717 ≫ 4352 ⇒ Minor GC
      • 5.2 进行Minor GC
        • 第二次GC ⇒ a1由from复制到to
        • 4096 > 1024 ⇒ sizeof(a3) > sizeof(Survivor) ⇒ a3晋升到Tenured区
      • 5.3 Minor GC之后,Eden空间充足,为a4分配4096K内存
        • 新生代 – 4436K

          • Eden:84K + 4096K(a4)
          • from(Survivor):0K
          • to(Survivor):256K(a1)
        • 老年代 – 8618K
          • Tenured:426K + 4096K(a2) + 4096K(a3)
  6. byte[] a5 = new byte[4 * _1MB];
    • Eden空间不足,不能为a5分配4096K内存,需要进行GC

      • 6.1 判定是否需要Full GC

        • 老年代未分配的连续空间:10240-427-4096-4096 = 1621K
        • 所有的新生代对象:sizeof(a1)+size(a2)=256+4096 = 4352K
        • 1621 ≪ 4352 ⇒ Minor GC + Full GC
      • 6.2 进行Minor GC
        • 第三次GC ⇒ a1由to复制到from
        • 4096 > 1621 ⇒ sizeof(a4) > remainingSpaceOf(Tenured) ⇒ a4无法晋升到Tenured区
      • 6.3 Minor GC之后,堆空间内存布局没有变化,Eden空间依旧不足,进行Full GC
        • 老年代减少了69K,忽略不计,依然保留着a2a3
        • 新生代增加了17k,忽略不计,依然保留着a1a4
      • 6.4 Full GC之后,新生代和老年代内存都不足以为a5分配内存,抛出OOM异常

2. 大对象直接进入老年代

2.1 说明

  • 大对象:需要大量连续内存空间的Java对象,如长字符串大数组

    • 代码中尽量避免使用短命大对象
  • -XX:PretenureSizeThreshold=?(Byte)
    • 新对象的大小大于PretenureSizeThreshold,直接分配在老年代
    • 作用
      • 新生代采用的是复制算法,避免Eden和Survivor之间的内存复制
      • 降低GC频率

2.2 代码

  1. public class TestPretenureSizeThreshold {
  2. private static final int _1MB = 1024 * 1024;
  3.  
  4. // JVM Args
  5. // -XX:+PrintGCDetails
  6. // -XX:+UseSerialGC
  7. // -Xms20m -Xmx20m
  8. // -Xmn10m -XX:SurvivorRatio=8
  9. // -XX:PretenureSizeThreshold=3145728
  10. public static void main(String[] args) {
  11. byte[] a1 = new byte[4 * _1MB]; // 直接分配到tenured
  12. byte[] a2 = new byte[4 * _1MB]; // 直接分配到tenured,无需GC
  13. }
  14. }

2.3 运行结果

  1. # 仅保留关键信息
  2.  
  3. Heap
  4. def new generation total 9216K, used 2302K
  5. eden space 8192K, 28% used
  6. from space 1024K, 0% used
  7. to space 1024K, 0% used
  8. tenured generation total 10240K, used 8192K
  9. the space 10240K, 80% used

2.4 运行过程解析

  • sizeof(a1) > PretenureSizeThreshold ⇒ 直接分配在Tenured
  • sizeof(a2) > PretenureSizeThreshold ⇒ 直接分配在Tenured
    • 如果不设置PretenureSizeThreshold,在分配a2前会产生一次Minor GC,最终a1Tenureda2Eden

3. 长期存活的对象晋升到老年代

3.1 说明

  • 对象年龄:对象每经历一次Minor GC,对象年龄加1
  • JVM参数:-XX:MaxTenuringThreshold=?,默认是15

3.2 代码

  1. public class TestTenuringThreshold {
  2. private static final int _1MB = 1024 * 1024;
  3. private static final int MAX_OBJ_AGE = 2;
  4.  
  5. // JVM Args
  6. // -XX:+PrintGCDetails
  7. // -XX:+UseSerialGC
  8. // -Xms20m -Xmx20m
  9. // -Xmn10m -XX:SurvivorRatio=8
  10. // -XX:MaxTenuringThreshold=3
  11. public static void main(String[] args) {
  12. System.out.println("===1. start full gc start===");
  13. System.gc();
  14. System.out.println("===1. start full gc end===\n");
  15.  
  16. System.out.println("===2. gc logs===");
  17. byte[] a1 = new byte[_1MB / 4];
  18. byte[] a2 = new byte[4 * _1MB];
  19. byte[] a3 = null;
  20. for (int i = 0; i < MAX_OBJ_AGE; ++i) {
  21. // 执行一次, a1的年龄加1 , 最终a1的年龄为MAX_OBJ_AGE
  22. // 如果 MAX_OBJ_AGE > MaxTenuringThreshold ⇒ a1 晋升到 Tenured
  23. // 如果 MAX_OBJ_AGE ≦ MaxTenuringThreshold ⇒ a1 停留在 Survivor
  24. a3 = null;
  25. a3 = new byte[4 * _1MB];
  26. }
  27. }
  28. }

3.3 运行结果

  1. # 仅保留关键信息
  2.  
  3. # MAX_OBJ_AGE = 3时
  4. ===1. start full gc start===
  5. [Full GC (System) [Tenured: 0K->430K(10240K)] 1974K->430K(19456K)
  6. ===1. start full gc end===
  7.  
  8. ===2. gc logs===
  9. [GC [DefNew: 4679K->256K(9216K)] 5110K->4783K(19456K)]
  10. [GC [DefNew: 4352K->256K(9216K)] 8879K->4783K(19456K)]
  11. [GC [DefNew: 4352K->256K(9216K)] 8879K->4783K(19456K)]
  12. Heap
  13. def new generation total 9216K, used 4516K
  14. eden space 8192K, 52% used
  15. from space 1024K, 25% used
  16. to space 1024K, 0% used
  17. tenured generation total 10240K, used 4526K
  18. the space 10240K, 44% used
  19.  
  20. # MAX_OBJ_AGE = 4时
  21. ===1. start full gc start===
  22. [Full GC (System) [Tenured: 0K->426K(10240K)] 1974K->426K(19456K)
  23. ===1. start full gc end===
  24.  
  25. ===2. gc logs===
  26. [GC [DefNew: 4843K->256K(9216K)] 5270K->4779K(19456K)]
  27. [GC [DefNew: 4437K->256K(9216K)] 8960K->4779K(19456K)]
  28. [GC [DefNew: 4409K->257K(9216K)] 8931K->4780K(19456K)]
  29. [GC [DefNew: 4391K->0K(9216K)] 8913K->4780K(19456K)]
  30. Heap
  31. def new generation total 9216K, used 4449K
  32. eden space 8192K, 54% used
  33. from space 1024K, 0% used
  34. to space 1024K, 0% used
  35. tenured generation total 10240K, used 4779K
  36. the space 10240K, 46% used

3.4 运行过程解析

  • a1a2现在Eden分配内存
  • 第一次为a3分配内存时,必须进行Minor GCa2被晋升Tenureda3分配在Edena1被复制到from(Survivor),此时a1的对象年龄为1
    • 随后每次循环中,先将a3置为nulla3原先引用的内存变成了垃圾,后续可回收
    • 再次为a3分配内存,此时Eden区内存不足,进行Minor GC
      • 目前存活的对象仅仅是a1,将其复制到to(另一块Survivor),a1的对象年龄加1
      • fromEden区清空,并在Eden为a3分配内存
  • MAX_OBJ_AGE = 3  MaxTenuringThreshold时
    • from space 1024K, 25% used ⇒ a1依旧停留在Survivor
  • MAX_OBJ_AGE = 4 > MaxTenuringThreshold时
    • [GC [DefNew: 4391K->0K(9216K)] 8913K->4780K(19456K)] ⇒ a1晋升到Tenured
    • from space 1024K, 0% used ⇒ Survivor已经无存活对象

4. 动态对象年龄判定

4.1 说明

  • 在Survivor中相同年龄(对象年龄 ≧ 2)的所有对象大小之和 ≧ 0.5 * sizeof(Survivor) ⇒ 大于或等于该年龄的对象直接晋升到老年代,无须考虑MaxTenuringThreshold

4.2 代码一(单个1/2对象)

  1. public class TestDynamicObjectAge {
  2. private static final int _1MB = 1024 * 1024;
  3. private static final int MAX_OBJ_AGE = 1;
  4.  
  5. // JVM Args
  6. // -XX:+PrintGCDetails
  7. // -XX:+UseSerialGC
  8. // -Xms20m -Xmx20m
  9. // -Xmn10m -XX:SurvivorRatio=8
  10. // -XX:MaxTenuringThreshold=3
  11. public static void main(String[] args) {
  12. int al;
  13. System.out.println("===1. start full gc start===");
  14. System.gc();
  15. System.out.println("===1. start full gc end===\n");
  16.  
  17. System.out.println("===2. gc logs===");
  18. byte[] a1 = new byte[_1MB / 2]; # 单个1/2对象
  19. byte[] a2 = new byte[4 * _1MB];
  20. byte[] a3 = null;
  21. for (int i = 0; i < MAX_OBJ_AGE; ++i) {
  22. // 相同年龄(对象年龄 ≧ 2)的所有对象大小之和 ≧ 0.5 * sizeof(Survivor)
  23. // ⇒ 大于或等于该年龄的对象直接晋升到老年代,无需考虑MaxTenuringThreshold
  24. a3 = null;
  25. a3 = new byte[4 * _1MB];
  26. }
  27. }
  28. }

4.3 代码一运行结果

  1. # 仅保留关键信息
  2.  
  3. # MAX_OBJ_AGE = 1时
  4. ===1. start full gc start===
  5. [Full GC (System) [Tenured: 0K->429K(10240K)] 1974K->429K(19456K)
  6. ===1. start full gc end===
  7.  
  8. ===2. gc logs===
  9. [GC [DefNew: 5099K->513K(9216K)] 5529K->5039K(19456K)]
  10. Heap
  11. def new generation total 9216K, used 4773K
  12. eden space 8192K, 52% used
  13. from space 1024K, 50% used # a1的对象年龄为1,依旧停留在Survivor区域
  14. to space 1024K, 0% used
  15. tenured generation total 10240K, used 4525K
  16. the space 10240K, 44% used
  17.  
  18. # MAX_OBJ_AGE = 2时
  19. ===1. start full gc start===
  20. [Full GC (System) [Tenured: 0K->426K(10240K)] 1974K->426K(19456K)
  21. ===1. start full gc end===
  22.  
  23. ===2. gc logs===
  24. [GC [DefNew: 5099K->513K(9216K)] 5526K->5036K(19456K)]
  25. [GC [DefNew: 4694K->1K(9216K)] 9216K->5037K(19456K)]
  26. # a1的对象年龄为2 < MaxTenuringThreshold,且a2等于0.5 * sizeof(Survivor),直接晋升到老年代
  27. Heap
  28. def new generation total 9216K, used 4317K
  29. eden space 8192K, 52% used
  30. from space 1024K, 0% used
  31. # a1的对象年龄为2 < MaxTenuringThreshold,且a2等于0.5 * sizeof(Survivor),直接晋升到老年代
  32. to space 1024K, 0% used
  33. tenured generation total 10240K, used 5036K
  34. the space 10240K, 49% used

4.4 代码二(四个1/8对象)

  1. public class TestDynamicObjectAge {
  2. private static final int _1MB = 1024 * 1024;
  3. private static final int MAX_OBJ_AGE = 1;
  4.  
  5. // JVM Args
  6. // -XX:+PrintGCDetails
  7. // -XX:+UseSerialGC
  8. // -Xms20m -Xmx20m
  9. // -Xmn10m -XX:SurvivorRatio=8
  10. // -XX:MaxTenuringThreshold=3
  11. public static void main(String[] args) {
  12. int al;
  13. System.out.println("===1. start full gc start===");
  14. System.gc();
  15. System.out.println("===1. start full gc end===\n");
  16.  
  17. System.out.println("===2. gc logs===");
  18. byte[] a1_1 = new byte[_1MB / 8]; # 四个1/8对象
  19. byte[] a1_2 = new byte[_1MB / 8];
  20. byte[] a1_3 = new byte[_1MB / 8];
  21. byte[] a1_4 = new byte[_1MB / 8];
  22. byte[] a2 = new byte[4 * _1MB];
  23. byte[] a3 = null;
  24. for (int i = 0; i < MAX_OBJ_AGE; ++i) {
  25. // 相同年龄(对象年龄 ≧ 2)的所有对象大小之和 ≧ 0.5 * sizeof(Survivor)
  26. // ⇒ 大于或等于该年龄的对象直接晋升到老年代,无需考虑MaxTenuringThreshold
  27. a3 = null;
  28. a3 = new byte[4 * _1MB];
  29. }
  30. }
  31. }

4.5 代码二运行结果

  1. # 仅保留关键信息
  2.  
  3. # MAX_OBJ_AGE = 1时
  4. ===1. start full gc start===
  5. [Full GC (System) [Tenured: 0K->427K(10240K)] 1974K->427K(19456K)
  6. ===1. start full gc end===
  7.  
  8. ===2. gc logs===
  9. [GC [DefNew: 4971K->514K(9216K)] 5399K->5038K(19456K)]
  10. Heap
  11. def new generation total 9216K, used 4859K
  12. eden space 8192K, 53% used # a1_1、a1_2、a1_3、a1_4的对象年龄为1,依旧停留在Survivor区域
  13. from space 1024K, 50% used
  14. to space 1024K, 0% used
  15. tenured generation total 10240K, used 4523K
  16. the space 10240K, 44% used
  17.  
  18. # MAX_OBJ_AGE = 2时
  19. ===1. start full gc start===
  20. [Full GC (System) [Tenured: 0K->427K(10240K)] 1974K->427K(19456K)
  21. ===1. start full gc end===
  22.  
  23. ===2. gc logs===
  24. [GC [DefNew: 4971K->514K(9216K)] 5399K->5037K(19456K)]
  25. [GC [DefNew: 4694K->0K(9216K)] 9218K->5037K(19456K)]
  26. # a1_1、a1_2、a1_3和a1_4的对象年龄为2 < MaxTenuringThreshold,且它们之和等于0.5 * sizeof(Survivor),晋升到老年代
  27. Heap
  28. def new generation total 9216K, used 4316K
  29. eden space 8192K, 52% used
  30. from space 1024K, 0% used
  31. # a1_1、a1_2、a1_3和a1_4的对象年龄为2 < MaxTenuringThreshold,且它们之和等于0.5 * sizeof(Survivor),晋升到老年代
  32. to space 1024K, 0% used
  33. tenured generation total 10240K, used 5037K
  34. the space 10240K, 49% used
  35. compacting perm gen total 21248K, used 4965K
  36. the space 21248K, 23% used

三、参考资料

窥探JVM内存分配和回收的过程的更多相关文章

  1. 最简单例子图解JVM内存分配和回收

    一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...

  2. 最简单例子图解JVM内存分配和回收(转)

    本文转自http://ifeve.com/a-simple-example-demo-jvm-allocation-and-gc/ http://www.idouba.net/a-simple-exa ...

  3. 图解JVM内存分配和回收

    一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...

  4. JVM内存分配与回收

    1.内存分配与回收策略 内存自动管理:自动化的解决了对象内存分配和回收对象内存的问题. 一般在堆上分配对象,也可能经过JTI编译后间接在栈上分配. 主要分配在新生代的Eden区,如果启动了本地线程分配 ...

  5. JVM内存分配和回收

    本文内容来自<Java编程思想(第四版)>第二章<一切都是对象>和第五章<初始化与清理>.作为一个使用了好几年的Javaer,再次看编程思想的前面章节(不要问我为什 ...

  6. jvm内存分配和回收策略

    在上一篇中,已经介绍了内存结构是什么样的. 这篇来介绍一下 内存是怎么分配的,和怎么回收的.(基本取自<深入理解Java虚拟机>一书) java技术体系中所提倡的自动内存管理最终可以归结为 ...

  7. JVM 内存分配和回收策略

    对象的内存分配,主要是在java堆上分配(有可能经过JIT编译后被拆为标量类型并间接地在栈上分配),如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配.少数情况下也是直接分配到老年代,分配规则不 ...

  8. A4. JVM 内存分配及回收策略

    [概述] Java 技术体系中所提倡的自动内存管理最终可以归结为自动化地解决两个问题:给对象分配内存以及回收分配给对象的内存. 对象的内存分配,往大方向讲,就是在堆上分配,对象主要分配在新生代的 Ed ...

  9. JVM——内存分配与回收策略

    1.对象优先在Eden区分配 大多数情况下,对象在新生代Eden区分配.当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC. 虚拟机提供了 -XX:+PrintGCDetails这 ...

随机推荐

  1. tomcat 内存配置

    在bin目录下的catalina.bat里添加如下代码: rem ----- Execute The Requested Command ------------------------------- ...

  2. tokudb引擎安装-2

    前言:因为现在tokuDB直接整合到Percona server里面了,下载页面直接跳转到下载Percona Server 页面了.安装方法跟以前不一样了,下面就来看一下新版本怎么安装了 ##准备阶段 ...

  3. [HDOJ2586]How far away?(最近公共祖先, 离线tarjan, 并查集)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 这题以前做过…现在用tarjan搞一发…竟然比以前暴力过的慢………… 由于是离线算法,需要Que ...

  4. input默认提示取消

    input 输入框有提示功能,当你之前输入过一些内容,你下次打入相关字符的时候,默认会有之前输入的一些相关的字符的提示,这个提示一般来说还是很好的,但是,有时候,我们想自己输入,不想要提示. 如果不需 ...

  5. std::ofstream由二进制流写文件的问题

    从MPQ包中读取二进制流出来然后文件写到硬盘. DWORD size = SFileGetSize(hFile); char* buffer = new char[size]; std::ofstre ...

  6. poj 1177 Picture (线段树 扫描线 离散化 矩形周长并)

    题目链接 题意:给出n个矩形,每个矩形给左下 和 右上的坐标,求围成的周长的长度. 分析: 首先感谢大神的博客,最近做题经常看大神的博客:http://www.cnblogs.com/kuangbin ...

  7. UVa 10892 (GCD) LCM Cardinality

    我一直相信这道题有十分巧妙的解法的,去搜了好多题解发现有的太过玄妙不能领会. 最简单的就是枚举n的所有约数,然后二重循环找lcm(a, b) = n的个数 #include <cstdio> ...

  8. LA 3516 (计数 DP) Exploring Pyramids

    设d(i, j)为连续子序列[i, j]构成数的个数,因为遍历从根节点出发最终要回溯到根节点,所以边界情况是:d(i, i) = 1; 如果s[i] != s[j], d(i, j) = 0 假设第一 ...

  9. 无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败。

    无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败. 在Asp.Net项目中使用WebClient或HttpWebRequest时出现以上错误 解决方案:把以下代码放在 ...

  10. Test语言编译器V0.8

    感觉这个挺好耍的,书上的代码有错误,而且功能有限. 一.词法分析 特点: (1)可对中文进行识别:(2)暂不支持负数,可以在读入‘-'时进行简单标记后就能对简单负数进行识别了. #include &l ...