ASM編譯問題"stack overflow"

背景

最先在騰訊Matrix中遇到這個編譯問題, 當時認爲是首包方法數超過了65536.
然而, 不斷精簡首包方法數依然不能解決. 因此確認和方法數無關.沒法解決, 只得放棄使用騰訊Matrixjava

使用ASM開發自研的插件, 也遇到一樣的"stack overflow"問題, 致使編譯失敗. 問題報錯以下:
數組





問題跟蹤

既然是自研的代碼, 有足夠的自由空間, 那必定能夠着手解決這個問題.jvm

根據異常堆棧提示, 在"ExecutionStack.java:168"位置拋出異常:
學習


看起來, 是由於某個計算的結果值超過了stack.length. 就主動拋出了異常"overflow"gradle

那麼這個stack是什麼? 其length是從哪裏來的?
繼續跟代碼可知是ExecutionStack初始化是給到的:
插件

繼續翻代碼, 發現這段:
3d

是Frame類初始化時傳入的值. 而這個 (int maxLocals, int maxStack)有點眼熟:
ASM裏注入方法時的 這個visitMaxs方法, 參數名一致, 彷佛問題就是出在這裏. 可是還須要驗證.



摸索修改

首先, visitMaxs()是在ASM中的, 而ExecutionStack是gradle中的, 二者來源其實並不一樣.
順代碼梳理, 應該很花時間. 簡單網搜了下, 都和jvm的==方法棧長度==有關, 看起來有點聯繫.code

其次, gradle的源碼是不能改的, 深刻的學習jvm得花點時間.
只有ASM的這個visitMaxs()方法, 咱們能夠重寫. 那就從visitMaxs()入手吧
重寫visitMaxs()的關鍵就是傳入正確的maxStack, 和maxLocals. 那麼==問題的關鍵就是如何正確傳入這兩個值==
使用AS的ASM Bytecode Outline插件能夠查看java文件的字節碼, 以此確認正確的值: cdn

確認值以後重寫visitMaxs()方法:
blog

嘗試編譯, 成功!





根本緣由

解決後總結根本緣由:

  1. 利用ASM插樁更改了方法的實際棧深度, 可是沒有同步更新正確的深度值
  2. ASM提供了ClassReader.EXPAND_FRAMES 和 ClassWriter.COMPUTE_MAXS來計算方法棧深度,
    可是在某些狀況下, ==ASM沒有計算出正確的方法棧深度==, 還很消耗編譯時間.
  3. 這個問題, 和方法數超過65536沒有任何關係.

PS: 另外還有一種狀況, 也是visitMaxs()傳入的參數不正確致使的:

小結

經驗上看, 利用ASM插樁碰到和數組長度相關的編譯異常, 能夠優先確認visitMaxs()傳入的值是否正確.

相關文章
相關標籤/搜索