2013-04-22 35 views
6

Tôi đã đọc rất nhiều bài viết xung đột liên quan đến cấp phát bộ nhớ khi chuỗi được tạo. Một số bài báo nói rằng toán tử mới tạo String trong heap và String literal được tạo trong String Pool [Heap] trong khi một số nói rằng toán tử mới tạo một đối tượng trong heap và một đối tượng khác trong chuỗi String.Tạo chuỗi và mảng mảng Phân bổ bộ nhớ

Để phân tích này tôi đã viết chương trình dưới đây mà in hashcode của String char mảng và đối tượng String:

import java.lang.reflect.Field; 

public class StringAnalysis { 

    private int showInternalCharArrayHashCode(String s) 
      throws SecurityException, NoSuchFieldException, 
      IllegalArgumentException, IllegalAccessException { 
     final Field value = String.class.getDeclaredField("value"); 
     value.setAccessible(true); 
     return value.get(s).hashCode(); 
    } 

    public void printStringAnalysis(String s) throws SecurityException, 
      IllegalArgumentException, NoSuchFieldException, 
      IllegalAccessException { 
     System.out.println(showInternalCharArrayHashCode(s)); 

     System.out.println(System.identityHashCode(s)); 

    } 

    public static void main(String args[]) throws SecurityException, 
      IllegalArgumentException, NoSuchFieldException, 
      IllegalAccessException, InterruptedException { 
     StringAnalysis sa = new StringAnalysis(); 
     String s1 = new String("myTestString"); 
     String s2 = new String("myTestString"); 
     String s3 = s1.intern(); 
     String s4 = "myTestString"; 

     System.out.println("Analyse s1"); 
     sa.printStringAnalysis(s1); 

     System.out.println("Analyse s2"); 
     sa.printStringAnalysis(s2); 

     System.out.println("Analyse s3"); 
     sa.printStringAnalysis(s3); 

     System.out.println("Analyse s4"); 
     sa.printStringAnalysis(s4); 

    } 

} 

này sản lượng bản in chương trình sau đây:

Analyse s1 
1569228633 
778966024 
Analyse s2 
1569228633 
1021653256 
Analyse s3 
1569228633 
1794515827 
Analyse s4 
1569228633 
1794515827 

Từ một đầu ra này điều rất rõ ràng là không phân biệt chuỗi được tạo ra như thế nào, nếu các chuỗi có cùng giá trị thì chúng sẽ chia sẻ cùng một mảng char.

Bây giờ câu hỏi của tôi là chararray này được lưu trữ ở đâu, nó được lưu trữ trong heap hoặc nó đi để permgen? Ngoài ra tôi muốn hiểu làm thế nào để diferentiate giữa các địa chỉ bộ nhớ heap và permgen địa chỉ bộ nhớ.

Tôi có vấn đề lớn nếu nó được lưu trữ trong permgen vì nó sẽ ăn hết không gian hạn chế quý giá của tôi. và nếu mảng char không được lưu trữ trong permgen nhưng trong heap thì nó ngụ ý rằng String literals cũng sử dụng không gian heap [đó là một cái gì đó tôi chưa bao giờ đọc].

+0

Trình biên dịch Java đơn giản là quá thông minh. Hãy thử '" ... ". ToCharArray()' hoặc như vậy. Nhưng sau đó mức thông tin chìm xuống không. –

+0

[this] (http://www.precisejava.com/javaperf/j2se/StringAndStringBuffer.htm) có thể hữu ích – Anirudha

+0

Nó sẽ thuyết phục hơn nếu bạn xây dựng một 'Chuỗi' từ một' StringBuilder', có lẽ bằng cách gọi riêng biệt thường xuyên để nối thêm các phần của giá trị chuỗi. –

Trả lời

2

Từ đầu ra này có một điều rất rõ ràng rằng bất kể như thế nào Chuỗi được tạo ra, nếu Strings có cùng một giá trị thì họ chia sẻ cùng một mảng char

Không khá: điều này xảy ra bởi vì bạn bắt đầu bằng một chuỗi chữ và tạo nhiều phiên bản từ nó. Trong triển khai OpenJDK (Sun/Oracle), mảng sao lưu sẽ được sao chép nếu nó đại diện cho toàn bộ chuỗi. Bạn có thể thấy điều này trong src.jar hoặc tại đây: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java#String.%3Cinit%3E%28java.lang.String%29

Nếu bạn cẩn thận tạo chuỗi nguồn của bạn sao cho chúng bắt đầu từ các mảng ký tự khác nhau, bạn sẽ thấy rằng chúng không chia sẻ mảng sao lưu.

Bây giờ câu hỏi của tôi là nơi được chararray này được lưu trữ

Để theo sự hiểu biết của tôi, các mảng ký tự cho một chuỗi chữ được lưu trữ trên heap (những người có kiến ​​thức tốt hơn về internals classloading, cảm thấy tự do để bình luận). Các chuỗi được tải từ các tệp sẽ luôn luôn lưu trữ các mảng sao lưu của chúng trên heap.

Điều tôi biết chắc chắn là cấu trúc dữ liệu được sử dụng bởi intern() chỉ tham chiếu đối tượng String, không phải mảng ký tự của nó.

+0

Tôi đã kiểm tra trong JLS, String đối tượng cho dù literals hoặc Strings mới thực sự được lưu trữ trong heap. Chuỗi nhóm chỉ là một tập hợp các tham chiếu. Vì vậy, mảng char thực sự đi đến đống. – Lokesh

1

Lần đầu tiên: Theo định nghĩa, "myTestString" theo nghĩa đen được tập trung, và tất cả các tham chiếu chuỗi nội bộ có cùng giá trị tham chiếu đến cùng một đối tượng Chuỗi vật lý. Vì vậy, chữ sẽ là CHÍNH XÁC NHƯ STRING là kết quả từ intern.

[Đã sửa] Theo định nghĩa, hashCode (nhưng không phải là identityHashCode) của hai Chuỗi có giá trị chuỗi ký tự giống nhau sẽ giống nhau.

HashCode của một mảng char[], mặt khác, chỉ đơn giản là một mớ hỗn độn của các bit địa chỉ của nó và không có liên quan đến nội dung của mảng. Điều này chỉ ra rằng mảng value là, trong tất cả các trường hợp trên, cùng một mảng chính xác.

(Thông tin thêm: Việc triển khai chuỗi cũ bao gồm một con trỏ đến char[], giá trị offset, độ dài và giá trị hashCode mới hơn. Giá trị chuỗi bị bắt đầu bằng phần tử 0 của mảng. Các triển khai khác (không phải là Sun/non-Oracle) loại bỏ mảng char[] riêng biệt và bao gồm các byte String bên trong phân bổ đống chính. Không có yêu cầu nào là trường value thực sự tồn tại.)

[Tiếp tục] Đã sao chép trường hợp thử nghiệm và thêm một vài dòng. hashCode và identityHashCode tạo ra các giá trị giống nhau trên một số char[] nhất định, nhưng tạo ra các giá trị khác nhau trên các mảng khác nhau có cùng nội dung.

Thực tế là các mảng giống hệt nhau trong s1 và s2 gần như chắc chắn bởi vì chúng đang chia sẻ mảng char[] của chữ "myTestString". Nếu các chuỗi được xây dựng riêng biệt từ các mảng "tươi" char[] thì chúng sẽ khác nhau.

Việc lấy đi chính từ tất cả điều này là các chuỗi ký tự được thực thi, và việc thực hiện đang được thử nghiệm "mượn" mảng của nguồn khi chuỗi được sao chép với new String(String).

Char array hash codes 
a1.hashCode() = 675303090 
a2.hashCode() = 367959235 
a1 identityHashCode = 675303090 
a2 identityHashCode = 367959235 
Strings from char arrays 
a1 String = ABCDE 
a1 String's hash = 62061635 
a1 String value's identityHashCode = 510044439 
a2 String = ABCDE 
a2 String's hash = 62061635 
a2 String value's identityHashCode = 1709651096 
+0

"Theo định nghĩa, String.hashCode và System.identityHashCode trên một String trả về cùng một giá trị" - bạn có một tham chiếu cho điều này? Bởi vì nó chắc chắn không phải là những gì [tài liệu] (http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#identityHashCode (java.lang.Object)) nói. – parsifal

+0

@parsifal - OK, bạn đã cho tôi ở đó - đọc sai thông số kỹ thuật một chút. identityHashCode có lẽ trả về phiên bản "xáo trộn địa chỉ" của băm và do đó sẽ xác định các đối tượng khác nhau (nhưng "giống hệt"). –

+0

@HotLicks: Nếu bạn thấy đầu ra, mã băm của mảng char giống nhau đối với tất cả các chuỗi. Vì vậy, điều này là không chính xác "Vì vậy, nó không có gì ngạc nhiên khi hai mảng băm đầu tiên là khác nhau" – Lokesh

3

Từ chuỗi src

public String(String original) { 
     this.value = original.value; 
     this.hash = original.hash; 
    } 

thì rõ ràng rằng chuỗi tạo ra với constructor này cổ phiếu mảng char (giá trị) với chuỗi gốc.

Điều quan trọng cần lưu ý là các API không đảm bảo chia sẻ này:

Khởi một đối tượng String mới được tạo ra để nó đại diện cho cùng một chuỗi ký tự như là đối số; nói cách khác, chuỗi mới được tạo ra là một bản sao của chuỗi đối số. Trừ khi một bản sao rõ ràng của bản gốc là cần thiết, sử dụng constructor này là không cần thiết vì Strings là bất biến

Ví dụ, String.substring được sử dụng để chia sẻ mảng char với chuỗi ban đầu, nhưng trong các phiên bản mới nhất của Java 1.7 String.substring tạo một bản sao của mảng char.

+0

FYI, ví dụ nguồn của bạn là từ phiên bản Java cũ hơn (1.5, tôi đoán). Nó dẫn đến một * rất nhiều * ngoại lệ bộ nhớ không mong muốn, đó là lý do tại sao các phiên bản hiện tại (1.6/1.7) nhìn vào kích thước của mảng sao lưu so với kích thước được báo cáo của chuỗi. – parsifal

+0

@Evgeniy: Điều đó giải thích tại sao mảng char giống nhau đối với tất cả các chuỗi được tạo bằng cách sử dụng mới và chia sẻ nó với chuỗi chữ. Có cách nào để kiểm tra nơi mảng char này được tạo ra trong heap hoặc permgen? – Lokesh

+1

nếu str == str.intern() nó có nghĩa là str là trong permgen –