Lombok 工具中@Data註解生成hashCode()可能導致StackOverflowError情況
阿新 • • 發佈:2018-12-22
看了大學一位同學部落格,寫了內容大致就是對於一個類中出現了該類的集合,通過Lombok的@Data註解生成class檔案,當建立兩個這個類的物件並且互相之間引用的時候,就出現了StackOverflowError異常,即棧溢位,或者叫超出棧深度.
在Java虛擬機器記憶體區域分為兩種一種是執行緒共享區域,另一種是執行緒私有區域,而虛擬機器棧就處線上程私有區域中,虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀,棧幀用於儲存區域性變量表,運算元棧,動態連結,方法返回地址等等。
Java 虛擬機器規範中,對這個區域規定了兩種異常情況:
- 如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲StackOverflowError異常。
- 如果虛擬機器在動態擴充套件棧時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常。
下面給出個記憶體區域記憶體溢位的簡單測試方法,
回到文章討論的問題,@Data註解在什麼情況下可能導致StackOverflowError情況呢?先看下同學給出的示例程式碼,
@Data public class Project { private從程式碼可以看出project1引用了project2,project2引用了project1,同學 給出的解釋如下:“ @Data 註解不僅幫我們實現了生成了getter/setter同時還重寫了equals(Object other) 和 hashCode()方法, Lombok 會將 Project 類中的 List projects 當做是 hashCode 計算的一部分(同理,equals,toString 也會存在同樣的問題),而如果我的專案中出現迴圈引用,這就會導致死迴圈,最終就會丟擲 StackOverFlowError。”,Long id; private String projectName; private List<Project> projects; public static void main(String[] args) { Project project1 = new Project(); Project project2 = new Project(); project1.setProjects(Arrays.asList(project2)); project2.setProjects(Arrays.asList(project1)); System.out.println(project1.hashCode()); } }
難道相互引用就一定出現死迴圈嗎?好吧,來看看反編譯下@Data到底幹啥了,程式碼反編譯後確實發現hashCode()方法被Lombok自己生成了,程式碼如下,
public int hashCode() { int PRIME = true; int result = 1; Object $id = this.getId(); int result = result * 59 + ($id == null ? 43 : $id.hashCode()); Object $projectName = this.getProjectName(); result = result * 59 + ($projectName == null ? 43 : $projectName.hashCode()); Object $projects = this.getProjects(); result = result * 59 + ($projects == null ? 43 : $projects.hashCode()); return result; }
那怎麼就死迴圈了呢,其實問題在於projects是一個ArrayList集合,而ArrayList對hashCode() 的計算會把每一個元素拿出來呼叫元素的hashCode()求和,但是projects裡面的元素是 project,project裡面又有projects,因此出現無限遞迴呼叫,又是單執行緒中呼叫方法,所以就丟擲StackOverflowError,在大多數場景中我們使用Lombok的@Data註解目的是為了生成getter/setter,不需要生成hashCode()和equals()方法,即使業務需要判斷兩個物件是否相等,邏輯基本也不會是lombok生成的那種,所以建議使用 @Getter 和 @Setter 替換 @Data註解
ArrayList的hashCode()程式碼如下:
public static int hashCode(Object a[]) { if (a == null) return 0; int result = 1; for (Object element : a) result = 31 * result + (element == null ? 0 : element.hashCode()); return result; }