這篇文章主要為大家展示了“Groovy腳本引發(fā)的 Old GC問(wèn)題怎么辦”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Groovy腳本引發(fā)的 Old GC問(wèn)題怎么辦”這篇文章吧。
示例代碼如下
ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("groovy"); String function = String.format("def getTargetParamValue(%s) {return \"%s\"}", "o", "$o"); engine.eval(function); Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("getTargetParamValue", "test-string"); System.out.println(result);
這段代碼定義了一個(gè)Groovy的方法,根據(jù)傳進(jìn)去的參數(shù)返回對(duì)應(yīng)的值。
由于生產(chǎn)環(huán)境流量很大,這段代碼被頻繁執(zhí)行。測(cè)試時(shí)的代碼如下
public class ScriptEngineTest { public static void main(String[] args) { ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("groovy"); //測(cè)試時(shí)改為死循環(huán) for (int i = 0;; i++) { try { String function = String.format("def getTargetParamValue(%s) {return \"%s\"}", "o", "$o"); engine.eval(function); Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("getTargetParamValue", "test-string"); System.out.println(result); TimeUnit.MICROSECONDS.sleep(100); System.out.println(new Date().toLocaleString()); } catch (Exception e) { String errorMsg = String.format("異常!%s", e.getMessage()); System.out.println(errorMsg); } } } }
模擬生產(chǎn)環(huán)境的情況,每秒鐘執(zhí)行10次。通過(guò)VusualVM觀察JVM
CPU使用情況,可以看到在每次堆內(nèi)存擴(kuò)容的時(shí)候,CPU使用量會(huì)有明顯增加
堆內(nèi)存使用情況
metaspace使用量一直在增加
類(lèi)加載情況,total loaded classes一直在增加
線(xiàn)程
dump內(nèi)存
可見(jiàn),每次循環(huán)中生成的 Groovy method在方法執(zhí)行完成之后并沒(méi)有被釋放掉,導(dǎo)致metaspace的使用量一直增加,最終撐爆JVM
針對(duì)以上問(wèn)題,解決方法為每次將生成的方法緩存下了,下次要執(zhí)行的時(shí)候從緩存中取。
private final ConcurrentHashMap<String, Invocable> concurrentHashMap = new ConcurrentHashMap<>(); private Object getInvokeResult(Object targetParam, String paramName, String expression) throws Exception { //targetParamClassName="com.umgsai.web.home.vo.NodeVO" String targetParamClassName = targetParam.getClass().getName(); //expression="$nodeVO.bizOwner" //paramName="nodeVO" String functionKey = String.format("%s_%s_%s", targetParamClassName, paramName, expression); functionKey = StringUtil.replaceChars(functionKey, "$", ""); functionKey = StringUtil.replaceChars(functionKey, ".", "_"); //functionKey為方法的名稱(chēng)和concurrentHashMap的key,這里需要去掉特殊字符 Invocable invocable = concurrentHashMap.get(functionKey); if (invocable != null) { //如果緩存中有,直接調(diào)用 return invocable.invokeFunction(functionKey, targetParam); } //如果緩存中沒(méi)有,生成方法,并且存到concurrentHashMap synchronized (lock) { invocable = concurrentHashMap.get(functionKey); if (invocable == null) { String function = String.format("def %s(%s) {return \"%s\"}", functionKey, paramName, expression); engine.eval(function); invocable = (Invocable) engine; concurrentHashMap.put(functionKey, invocable); if (log.isInfoEnabled()) { String msg = String.format("Create new Groovy function, functionKey=%s, paramName=%s, expression=%s", functionKey, paramName, expression); log.info(msg); } } } if (log.isInfoEnabled()) { log.info(String.format("Groovy function concurrentHashMap.size=%d", concurrentHashMap.size())); } return invocable.invokeFunction(functionKey, targetParam); }
以上是“Groovy腳本引發(fā)的 Old GC問(wèn)題怎么辦”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。