日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Groovy与Java集成常见的坑--转

發(fā)布時(shí)間:2025/4/5 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Groovy与Java集成常见的坑--转 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

https://yq.aliyun.com/articles/2357

groovy特性

Groovy是一門基于JVM的動(dòng)態(tài)語言,同時(shí)也是一門面向?qū)ο蟮恼Z言,語法上和Java非常相似。它結(jié)合了Python、Ruby和Smalltalk的許多強(qiáng)大的特性,Groovy 代碼能夠與 Java 代碼很好地結(jié)合,也能用于擴(kuò)展現(xiàn)有代碼。

Java作為一種通用、靜態(tài)類型的編譯型語言有很多優(yōu)勢(shì),但同樣存在一些負(fù)擔(dān):

  • 重新編譯太費(fèi)工;
  • 靜態(tài)類型不夠靈活,重構(gòu)起來時(shí)間可能比較長;
  • 部署的動(dòng)靜太大;
  • java的語法天然不適用生產(chǎn)dsl;

相對(duì)于Java,它在編寫代碼的靈活性上有非常明顯的提升,對(duì)于一個(gè)長期使用Java的開發(fā)者來說,使用Groovy時(shí)能夠明顯地感受到負(fù)身上的“枷鎖”輕了。Groovy是動(dòng)態(tài)編譯語言,廣泛用作腳本語言和快速原型語言,主要優(yōu)勢(shì)之一就是它的生產(chǎn)力。Groovy 代碼通常要比 Java 代碼更容易編寫,而且編寫起來也更快,這使得它有足夠的資格成為開發(fā)工作包中的一個(gè)附件。

Java不是解決動(dòng)態(tài)層問題的理想語言,這些動(dòng)態(tài)層問題包括原型設(shè)計(jì)、腳本處理等。可以把Groovy看作給Java靜態(tài)世界補(bǔ)充動(dòng)態(tài)能力的語言,同時(shí)Groovy已經(jīng)實(shí)現(xiàn)了java不具備的語言特性:

  • 函數(shù)字面值;
  • 對(duì)集合的一等支持;
  • 對(duì)正則表達(dá)式的一等支持;
  • 對(duì)xml的一等支持;

groovy與java集成的方式

重溫下Groovy調(diào)用Java方式,包括使用GroovyClassLoader、GroovyShell和GroovyScriptEngine。

GroovyClassLoader

用 Groovy 的 GroovyClassLoader ,動(dòng)態(tài)地加載一個(gè)腳本并執(zhí)行它的行為。GroovyClassLoader是一個(gè)定制的類裝載器,負(fù)責(zé)解釋加載Java類中用到的Groovy類。

GroovyClassLoader loader = new GroovyClassLoader(); Class groovyClass = loader.parseClass(new File(groovyFileName)); GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance(); groovyObject.invokeMethod("run", "helloworld");

GroovyShell

GroovyShell允許在Java類中(甚至Groovy類)求任意Groovy表達(dá)式的值。您可使用Binding對(duì)象輸入?yún)?shù)給表達(dá)式,并最終通過GroovyShell返回Groovy表達(dá)式的計(jì)算結(jié)果。

GroovyShell shell = new GroovyShell(); Script groovyScript = shell.parse(new File(groovyFileName)); Object[] args = {}; groovyScript.invokeMethod("run", args);

GroovyScriptEngine

GroovyShell多用于推求對(duì)立的腳本或表達(dá)式,如果換成相互關(guān)聯(lián)的多個(gè)腳本,使用GroovyScriptEngine會(huì)更好些。GroovyScriptEngine從您指定的位置(文件系統(tǒng),URL,數(shù)據(jù)庫,等等)加載Groovy腳本,并且隨著腳本變化而重新加載它們。如同GroovyShell一樣,GroovyScriptEngine也允許您傳入?yún)?shù)值,并能返回腳本的值。

Groovy代碼文件與class文件的對(duì)應(yīng)關(guān)系

而作為基于JVM的語言,Groovy可以非常容易的和Java進(jìn)行互操作,但也需要編譯成class文件后才能運(yùn)行,所以了解Groovy代碼文件和class文件的對(duì)應(yīng)關(guān)系,有助于更好地理解Groovy的運(yùn)行方式和結(jié)構(gòu)。

對(duì)于沒有任何類定義

如果Groovy腳本文件里只有執(zhí)行代碼,沒有定義任何類(class),則編譯器會(huì)生成一個(gè)Script的子類,類名和腳本文件的文件名一樣,而腳本的代碼會(huì)被包含在一個(gè)名為run的方法中,同時(shí)還會(huì)生成一個(gè)main方法,作為整個(gè)腳本的入口。

對(duì)于僅有一個(gè)類

如果Groovy腳本文件里僅含有一個(gè)類,而這個(gè)類的名字又和腳本文件的名字一致,這種情況下就和Java是一樣的,即生成與所定義的類一致的class文件。

對(duì)于多個(gè)類

如果Groovy腳本文件含有多個(gè)類,groovy編譯器會(huì)很樂意地為每個(gè)類生成一個(gè)對(duì)應(yīng)的class文件。如果想直接執(zhí)行這個(gè)腳本,則腳本里的第一個(gè)類必須有一個(gè)static的main方法。

groovy與java集成中經(jīng)常出現(xiàn)的問題

使用GroovyShell的parse方法導(dǎo)致perm區(qū)爆滿的問題

如果應(yīng)用中內(nèi)嵌Groovy引擎,會(huì)動(dòng)態(tài)執(zhí)行傳入的表達(dá)式并返回執(zhí)行結(jié)果,而Groovy每執(zhí)行一次腳本,都會(huì)生成一個(gè)腳本對(duì)應(yīng)的class對(duì)象,并new一個(gè)InnerLoader去加載這個(gè)對(duì)象,而InnerLoader和腳本對(duì)象都無法在gc的時(shí)候被回收運(yùn)行一段時(shí)間后將perm占滿,一直觸發(fā)fullgc。

  • 為什么Groovy每執(zhí)行一次腳本,都會(huì)生成一個(gè)腳本對(duì)應(yīng)的class對(duì)象?

一個(gè)ClassLoader對(duì)于同一個(gè)名字的類只能加載一次,都由GroovyClassLoader加載,那么當(dāng)一個(gè)腳本里定義了C這個(gè)類之后,另外一個(gè)腳本再定義一個(gè)C類的話,GroovyClassLoader就無法加載了。為什么這里會(huì)每次執(zhí)行都會(huì)加載?

這是因?yàn)閷?duì)于同一個(gè)groovy腳本,groovy執(zhí)行引擎都會(huì)不同的命名,且命名與時(shí)間戳有關(guān)系。當(dāng)傳入text時(shí),class對(duì)象的命名規(guī)則為:"script" + System.currentTimeMillis() + Math.abs(text.hashCode()) + ".groovy"。這就導(dǎo)致就算groovy腳本未發(fā)生任何變化,每次執(zhí)行parse方法都會(huì)新生成一個(gè)腳本對(duì)應(yīng)的class對(duì)象,且由GroovyClassLoader進(jìn)行加載,不斷增大perm區(qū)。

  • 為什么InnerLoader加載的對(duì)應(yīng)無法通過gc清理掉?

大家都知道,JVM中的Class只有滿足以下三個(gè)條件,才能被GC回收,也就是該Class被卸載:1. 該類所有的實(shí)例都已經(jīng)被GC,也就是JVM中不存在該Class的任何實(shí)例;2. 加載該類的ClassLoader已經(jīng)被GC;3. 該類的java.lang.Class對(duì)象沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法。

在GroovyClassLoader代碼中有一個(gè)class對(duì)象的緩存,進(jìn)一步跟下去,發(fā)現(xiàn)每次編譯腳本時(shí)都會(huì)在Map中緩存這個(gè)對(duì)象,即:setClassCacheEntry(clazz)。每次groovy編譯腳本后,都會(huì)緩存該腳本的Class對(duì)象,下次編譯該腳本時(shí),會(huì)優(yōu)先從緩存中讀取,這樣節(jié)省掉編譯的時(shí)間。這個(gè)緩存的Map由GroovyClassLoader持有,key是腳本的類名,這就導(dǎo)致每個(gè)腳本對(duì)應(yīng)的class對(duì)象都存在引用,無法被gc清理掉。

  • 如何解決?

請(qǐng)參考:Groovy引發(fā)的PermGen區(qū)爆滿問題定位與解決。

如需更深入的理解GroovyClassLoader體系,請(qǐng)參考下面這篇文章Groovy深入探索——Groovy的ClassLoader體系

使用GroovyClassLoader加載機(jī)制導(dǎo)致頻繁gc問題

通常使用如下代碼在Java 中執(zhí)行 Groovy 腳本:

GroovyClassLoader groovyLoader = new GroovyClassLoader(); Class<Script> groovyClass = (Class<Script>) groovyLoader.parseClass(groovyScript); Script groovyScript = groovyClass.newInstance();

每次執(zhí)行g(shù)roovyLoader.parseClass(groovyScript),Groovy 為了保證每次執(zhí)行的都是新的腳本內(nèi)容,會(huì)每次生成一個(gè)新名字的Class文件,這個(gè)點(diǎn)已經(jīng)在前文中說明過。當(dāng)對(duì)同一段腳本每次都執(zhí)行這個(gè)方法時(shí),會(huì)導(dǎo)致的現(xiàn)象就是裝載的Class會(huì)越來越多,從而導(dǎo)致PermGen被用滿。
同時(shí)這里也存在性能瓶頸問題,如果去分析這段代碼會(huì)發(fā)現(xiàn)90%的耗時(shí)占用在Class

為了避免這一問題通常做法是緩存Script對(duì)象,從而避免以上2個(gè)問題。在這過程中通常又會(huì)引入新的問題:

  • 高并發(fā)情況下,binding對(duì)象混亂導(dǎo)致計(jì)算出錯(cuò)

在高并發(fā)的情況下,在執(zhí)行賦值binding對(duì)象后,真正執(zhí)行run操作時(shí),拿到的binding對(duì)象可能是其它線程賦值的對(duì)象,所以出現(xiàn)數(shù)據(jù)計(jì)算混亂的情況

  • 長時(shí)間運(yùn)行仍然出現(xiàn)oom,無法解決Class

這點(diǎn)在上文中已經(jīng)提到,由于groovyClassLoader會(huì)緩存每次編譯groovy腳本的Class對(duì)象,下次編譯該腳本時(shí),會(huì)優(yōu)先從緩存中讀取,這樣節(jié)省掉編譯的時(shí)間。導(dǎo)致被加載的Class對(duì)象因?yàn)榇嬖谝枚鵁o法被卸載,雖然通過緩存避免了短時(shí)間內(nèi)大量生成新的class對(duì)象,但如果長時(shí)間運(yùn)營仍然會(huì)存在問題。

比較好的做法是:

  • 每個(gè) script 都 new 一個(gè) GroovyClassLoader 來裝載;
  • 對(duì)于 parseClass 后生成的 Class 對(duì)象進(jìn)行cache,key 為 groovyScript 腳本的md5值。

CodeCache用滿,導(dǎo)致JIT禁用問題

對(duì)于大量使用Groovy的應(yīng)用,尤其是 Groovy 腳本還會(huì)經(jīng)常更新的應(yīng)用,由于這些Groovy腳本在執(zhí)行了很多次后都會(huì)被JVM編譯為 native 進(jìn)行優(yōu)化,會(huì)占據(jù)一些 CodeCache 空間,而如果這樣的腳本很多的話,可能會(huì)導(dǎo)致 CodeCache 被用滿,而 CodeCache 一旦被用滿,JVM 的 Compiler 就會(huì)被禁用,那性能下降的就不是一點(diǎn)點(diǎn)了

參考文獻(xiàn)

Groovy引發(fā)的PermGen區(qū)爆滿問題定位與解決
groovy腳本導(dǎo)致的FullGC問題
Groovy性能問題
Groovy的classloader加載機(jī)制喚起的頻繁GC
Groovy深入探索——Groovy的ClassLoader體系

轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/8405699.html

總結(jié)

以上是生活随笔為你收集整理的Groovy与Java集成常见的坑--转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。