結合代碼和記憶體變化圖一步步弄懂JVM的FullGC

来源:https://www.cnblogs.com/bingo24/archive/2023/02/27/17159806.html
-Advertisement-
Play Games

1.年輕代存活的對象太多,老年代了放不下 01.示例代碼 public class DemoTest1 { public static void main(String[] args) { byte[] array1 = new byte[4 * 1024 * 1024]; array1 = nul ...


1.年輕代存活的對象太多,老年代了放不下

01.示例代碼

public class DemoTest1 {
    public static void main(String[] args) {
        byte[] array1 = new byte[4 * 1024 * 1024];
        array1 = null;

        byte[] array2 = new byte[2 * 1024 * 1024];
        byte[] array3 = new byte[2 * 1024 * 1024];
        byte[] array4 = new byte[2 * 1024 * 1024];
        byte[] array5 = new byte[128 * 1024];

        byte[] array6 = new byte[2 * 1024 * 1024];

    }

02.啟動JVM參數

-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中,參數-XX:PretenureSizeThreshold,參數要設置大對象閾值為3MB,也就是超過3MB,就直接進入老年代。

大對象大小是3MB。一旦對象大小超過3MB,不會進入新生代,直接進入老年代。

啟動命令:

java  -jar -XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8  -XX:MaxTenuringThre
shold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log jvm-demo.jar

03.GC日誌

啟動之後就得到如下GC日誌:

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16703268k(7458748k free), swap 23781156k(9784196k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC 
0.174: [GC (Allocation Failure) 0.174: [ParNew (promotion failed): 7457K->8328K(9216K), 0.0046949 secs]
0.179: [CMS: 8194K->6962K(10240K), 0.0033396 secs] 11553K->6962K(19456K), [Metaspace: 2970K->2970K(1056768K)], 0.0089224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 par new generation   total 9216K, used 2130K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  26% used [0x00000000fec00000, 0x00000000fee14930, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 concurrent mark-sweep generation total 10240K, used 6962K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 330K, capacity 386K, committed 512K, reserved 1048576K

04.分析GC日誌

先看如下代碼:

 byte[] array1 = new byte[4 * 1024 * 1024];
        array1 = null;

這行代碼直接分配了一個4MB的大對象,此時這個對象會直接進入老年代,接著array1不再引用這個對象。

此時記憶體分配如下:

緊接著就是如下代碼

byte[] array2 = new byte[2 * 1024 * 1024];
        byte[] array3 = new byte[2 * 1024 * 1024];
        byte[] array4 = new byte[2 * 1024 * 1024];
        byte[] array5 = new byte[128 * 1024];

連續分配了4個數組,其中3個是2MB的數組,1個是128KB的數組,如下圖所示,全部會進入Eden區域中。

接著會執行如下代碼:byte[] array6 = new byte[2 * 1024 * 1024];。此時還能放得下2MB的對象嗎?

不可能了,因為Eden區已經放不下了。因此此時會直接觸發一次Young GC。

我們看下麵的GC日誌:

0.174: [GC (Allocation Failure) 0.174: [ParNew (promotion failed): 7457K->8328K(9216K), 0.0046949 secs]

這行日誌顯示了,Eden區原來是有7000多KB的對象,但是回收之後發現一個都回收不掉,因為上述幾個數組都被變數引用了。

所以此時,一定會直接把這些對象放入到老年代里去,但是此時老年代里已經有一個4MB的數組了,還能放的下3個2MB的數組和1個128KB的數組嗎?

明顯是不行的,此時一定會超過老年代的10MB大小。

所以此時看gc日誌:

0.179: [CMS: 8194K->6962K(10240K), 0.0033396 secs] 11553K->6962K(19456K), [Metaspace: 2970K->2970K(1056768K)], 0.0089224 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

此時執行了CMS垃圾回收器的Full GC,Full GC其實就是會對老年代進行Old GC,同時一般會跟一次Young GC關聯,還會觸發一次元數據區(永久代)的GC。

在CMS Full GC之前,就已經觸發過Young GC了,此時可以看到此時Young GC就已經有了,接著就是執行針對老年代的Old GC,也就是如下日誌:

CMS: 8194K->6962K(10240K), 0.0033396 secs

這裡看到老年代從8MB左右的對象占用,變成了6MB左右的對象占用,這是怎麼個過程呢?

很簡單,一定是在Young GC之後,先把2個2MB的數組放入了老年代,如下圖。

此時要繼續放1個2MB的數組和1個128KB的數組到老年代,一定會放不下,所以此時就會觸發CMS的Full GC。

然後此時就會回收掉其中的一個4MB的數組,因為他已經沒人引用了,如下圖所示。

所以再看CMS的垃圾回收日誌:CMS: 8194K->6962K(10240K), 0.0033396 secs,他是從回收前的8MB變成了6MB,就是上圖所示。

最後在CMS Full GC執行完畢之後,其實年輕代的對象都進入了老年代,此時最後一行代碼要在年輕代分配2MB的數組就可以成功了,如下圖。

05.總結

這是一個觸發老年代GC的案例,就是年輕代存活的對象太多放不下老年代了,此時就會觸發CMS的Full GC。

2.老年代可用空間小於了歷次Young GC後升入老年代的對象的平均大小

01.示例代碼

public class DemoTest1 {
    public static void main(String[] args) {
        byte[] array1 = new byte[1 * 1024 * 1024];
        array1 = null;
        byte[] array2 = new byte[1 * 1024 * 1024];
        array2 = null;
        byte[] array3 = new byte[1 * 1024 * 1024];
        array3 = null;
        byte[] array4 = new byte[1 * 1024 * 1024];//觸發YGC 1MB    1

        array1 = new byte[1 * 1024 * 1024];
        array1 = null;
        array2 = new byte[1 * 1024 * 1024];
        array2 = null;
        array3 = new byte[1 * 1024 * 1024];//觸發YGC   Y 1MB O 1MB  2
        array3 = null;

        byte[] array5 = new byte[1 * 1024 * 1024];// Y 2MB  O 1MB
        array1 = new byte[1 * 1024 * 1024];// Y 3MB
        array1 = null;
        array2 = new byte[1 * 1024 * 1024];// Y 1MB  O 2MB YGC   3
        
        array2 = null;
        array3 = new byte[1 * 1024 * 1024];//Y 2MB  O 2MB
        array3 = null;
        byte[] array6 = new byte[1 * 1024 * 1024];//Y 3MB  O 2MB
        array1 = new byte[1 * 1024 * 1024];//Y 1MB  O 3MB YGC  4
        
        array1 = null;
        array2 = new byte[1 * 1024 * 1024];//Y 2MB
        array2 = null;
        array3 = new byte[1 * 1024 * 1024];//Y 3MB
        array3 = null;
        byte[] array7 = new byte[1 * 1024 * 1024];//YGC  5

    }
}

02.啟動JVM參數

-XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

其中,參數-XX:PretenureSizeThreshold,參數要設置大對象閾值為2MB,也就是超過2MB,就直接進入老年代。

大對象大小是3MB。一旦對象大小超過3MB,不會進入新生代,直接進入老年代。

啟動命令:

java  -jar -XX:NewSize=5M -XX:MaxNewSize=5M -XX:InitialHeapSize=10M -XX:MaxHeapSize=10M -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=2M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log jvm-demo.jar

03.GC日誌

啟動之後就得到如下GC日誌:

老年代

年輕代

Java HotSpot(TM) 64-Bit Server VM (25.151-b12) for windows-amd64 JRE (1.8.0_151-b12), built on Sep  5 2017 19:33:46 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16703268k(7221016k free), swap 23781156k(8613656k free)
CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=5242880 -XX:MaxTenuringThreshold=15 -XX:NewSize=5242880 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=2097152 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC 
0.121: [GC (Allocation Failure) 0.121: [ParNew: 3155K->512K(4608K), 0.0041165 secs] 3155K->766K(9728K), 0.0042644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.125: [GC (Allocation Failure) 0.125: [ParNew: 3663K->0K(4608K), 0.0016667 secs] 3917K->1732K(9728K), 0.0017448 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.127: [GC (Allocation Failure) 0.127: [ParNew: 3142K->0K(4608K), 0.0013221 secs] 4875K->2756K(9728K), 0.0013592 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.129: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2756K(5120K)] 4878K(9728K), 0.0004498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.129: [CMS-concurrent-mark-start]
0.130: [GC (Allocation Failure) 0.130: [ParNew: 3146K->0K(4608K), 0.0005869 secs] 5902K->2756K(9728K), 0.0006362 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.131: [GC (Allocation Failure) 0.131: [ParNew: 3148K->0K(4608K), 0.0007974 secs] 5904K->3780K(9728K), 0.0008262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 4608K, used 2207K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
  eden space 4096K,  53% used [0x00000000ff600000, 0x00000000ff827f38, 0x00000000ffa00000)
  from space 512K,   0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
  to   space 512K,   0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
 concurrent mark-sweep generation total 5120K, used 3780K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 330K, capacity 386K, committed 512K, reserved 1048576K

04.分析GC日誌

(1).代碼塊1

先看如下代碼:

  byte[] array1 = new byte[1 * 1024 * 1024];
  array1 = null;
  byte[] array2 = new byte[1 * 1024 * 1024];
  array2 = null;
  byte[] array3 = new byte[1 * 1024 * 1024];
  array3 = null;
  byte[] array4 = new byte[1 * 1024 * 1024];

這段代碼直接分配了4個1MB的數組,並且在第4個數組的時候,會因為新生代記憶體不足觸發YGC。

此時記憶體分配如下:

對應如下GC日誌:

0.121: [GC (Allocation Failure) 0.121: [ParNew: 3155K->512K(4608K), 0.0041165 secs] 3155K->766K(9728K), 0.0042644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

此時,可以看到新生代就只剩512K的對象,這個奇怪的512KB的對象進入Survivor From區。

那麼大小為1MB的數組對象去哪裡呢?肯定不是這個奇怪的512KB的對象。

這1MB的數組首先肯定是準備進入Survivor From區,可是,在我們設置的JVM參數下,只有0.5MB,明顯是不夠分配的。根據JVM YoungGC的規則,Survivor區放不下GC之後存活的對象,直接進入老年代

所以,1MB的數組對象是直接進入到老年代了。

此時,記憶體分配如下:

(2).代碼塊2

緊接這就是這塊代碼:

 array1 = new byte[1 * 1024 * 1024];
 array1 = null;
 array2 = new byte[1 * 1024 * 1024];
 array2 = null;
 array3 = new byte[1 * 1024 * 1024];

這裡再次創建了3個1MB的數組對象,並且會觸發一次YoungGC;

對應 GC日誌如下:

0.125: [GC (Allocation Failure) 0.125: [ParNew: 3663K->0K(4608K), 0.0016667 secs] 3917K->1732K(9728K), 0.0017448 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

此時,Young GC之後,新生代變成0KB,那麼存活的大小為1MB的數組對象去哪裡呢?

這1MB的數組首先肯定是準備進入Survivor From區,可是,在我們設置的JVM參數下,只有0.5MB,明顯是不夠分配的。根據JVM YoungGC的規則,Survivor區放不下GC之後存活的對象,直接進入老年代

所以,1MB的數組對象是直接進入到老年代了。

之前看到的未知的對象512KB也進入到老年代,此時記憶體分配如下:

(3).代碼塊3
array3 = null;
byte[] array5 = new byte[1 * 1024 * 1024];
array1 = new byte[1 * 1024 * 1024];
array1 = null;
array2 = new byte[1 * 1024 * 1024];

這裡再次創建了3個1MB的數組對象,並且會觸發一次YoungGC;

對應的GC日誌如下:

0.127: [GC (Allocation Failure) 0.127: [ParNew: 3142K->0K(4608K), 0.0013221 secs] 4875K->2756K(9728K), 0.0013592 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

此時記憶體分配如下:

(4).代碼塊4
array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 2MB  O 2MB
array3 = null;
byte[] array6 = new byte[1 * 1024 * 1024];
array1 = new byte[1 * 1024 * 1024];

這裡再次創建了3個1MB的數組對象,並且會觸發一次YoungGC;並且在這兒,觸發Young GC之前觸發了一次CMS的Old GC,觸發的條件就是老年代可用空間小於了歷次Young GC後升入老年代的對象的平均大小。此時新生代大小變成0KB

對應的GC日誌如下:

0.129: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2756K(5120K)] 4878K(9728K), 0.0004498 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.129: [CMS-concurrent-mark-start]
0.130: [GC (Allocation Failure) 0.130: [ParNew: 3146K->0K(4608K), 0.0005869 secs] 5902K->2756K(9728K), 0.0006362 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

此時記憶體分配如下:

(5).代碼塊5
array1 = null;
array2 = new byte[1 * 1024 * 1024];//Y 2MB
array2 = null;
array3 = new byte[1 * 1024 * 1024];//Y 3MB
array3 = null;
byte[] array7 = new byte[1 * 1024 * 1024];

此時,再創建3個1MB的數組對象,再次觸發一次Young GC,執行完YoungGC,此時新生代大小變成0KB;

對應的GC日誌如下:

0.131: [GC (Allocation Failure) 0.131: [ParNew: 3148K->0K(4608K), 0.0007974 secs] 5904K->3780K(9728K), 0.0008262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

此時記憶體分配如下:

(6).總結

如下GC堆記憶體日誌我們也可以去驗證下上面的推測:

此時新生代使用了53%的大小,我們還有一個1MB的數組,可能還存在一些未知對象。

在老年代中使用了大約3MB的空間,應該就是上圖中的對象。

Heap
 par new generation   total 4608K, used 2207K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
  eden space 4096K,  53% used [0x00000000ff600000, 0x00000000ff827f38, 0x00000000ffa00000)
  from space 512K,   0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
  to   space 512K,   0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
 concurrent mark-sweep generation total 5120K, used 3780K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 2976K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 330K, capacity 386K, committed 512K, reserved 1048576K

3.幾個觸發Full GC的條件

第一:是老年代可用記憶體小於新生代全部對象的大小,如果沒開啟空間擔保參數,會直接觸發Full GC,所以一般空間擔保參數都會打開;註:jDK1.8之後已經取消了-XX:-HandlePromotionFailure 機制

第二:是老年代可用記憶體小於歷次新生代GC後進入老年代的平均對象大小,此時會提前Full GC;

第三:是新生代Minor GC後的存活對象大於Survivor,那麼就會進入老年代,此時老年代記憶體不足。

上述情況都會導致老年代Full GC。

第四:就是“-XX:CMSInitiatingOccupancyFaction”參數,

如果老年代可用記憶體大於歷次新生代GC後進入老年代的對象平均大小,但是老年代已經使用的記憶體空間超過了這個參數指定的比例,也會自動觸發Full GC。預設92%


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有關Apifox軟體之前寫過一篇文章: 介面測試神器Apifox,親測好用! 如何一鍵自動生成資料庫文檔之前也寫過一篇文章: 資料庫界的Swagger:一鍵生成資料庫文檔! 一、Apifox插件的優勢 作為一名後端開發在項目開發過程中,肯定需要提供介面文檔。 一般我們有兩種方案 項目結合Swagge ...
  • 歡迎關註個人公眾號:愛喝可可牛奶 LeetCode演算法訓練 93.複原IP地址 78.子集 90.子集II LeetCode 93. 複原 IP 地址 分析 字元串全部由數字組成,ipv4每一段數字不能有前導0,且大小∈[0,255] 等價於將字元串進行分割,並判斷分割後的數是否滿足條件 插入一個點 ...
  • 一、低配logging 日誌總共分為以下五個級別,這個五個級別自下而上進行匹配 debug-->info-->warning-->error-->critical,預設最低級別為warning級別。 1.v1 import logging logging.debug('調試信息') logging. ...
  • 當前IDEA的版本,已經更新到2022.3.x,本文為大家帶來最新版IDEA的激活破解方式。此激活方式,支持PyCharm、WebStorm、PhpStorm、RubyMine等Jetbrains家的其他同類IDE。此激活工具,向下相容,即 2022.3 之前的版本,也都可以使用此方式激活。 ...
  • Android ADB 備忘清單 ADB,Android Debug Bridge,包含在 Google 的 Android SDK 中,可用於從電腦控制您的 Android 設備。以下是您可以與 ADB 一起使用的一些最常見的命令及其用法入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 ...
  • 背景 大家好,我是棧長。 前些天,棧長給大家分享了兩篇有意思的文章: 帶了一個 3 年的開發,不會迴圈刪除 List 中的元素,我簡直崩潰!! 面試官:怎麼去除 List 中的重覆元素?我一行代碼搞定,趕緊拿去用! 這兩篇文章確實能幫助一大部分人,其中分享的一些實現技巧,編程很多年的高手也不一定用過 ...
  • Java方法:方法的重載、命令行傳參、遞歸 方法的重載 重載就是在一個類中,有相同的函數名稱,但是形參不同的函數。 方法重載的規則: 方法名稱必須相同; 參數列表必須不同(個數不同或者類型不同或者參數排列順序不同等); 方法的返回類型可以相同也可以不相同; 僅僅返回類型不同不足以成為方法的重載。 實 ...
  • 問題描述: 編譯器無法使用cmd命令來執行py文件,結果可能報ModuleNotFoundError的錯誤。 比如在cmd任務欄執行:python E:\myProgram\Python\spider\NetDealer\core\others\audi.py,項目NetDealer 包core 文 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...