2010-01-27 29 views
189

Tôi đã sử dụng biến có nhiều dữ liệu trong đó, nói String data. Tôi muốn sử dụng một phần nhỏ trong chuỗi này theo cách sau:Tại sao phụ thêm "" vào bộ nhớ lưu chuỗi?

this.smallpart = data.substring(12,18); 

Sau một vài giờ gỡ lỗi (với một visualizer bộ nhớ) tôi phát hiện ra rằng các đối tượng lĩnh vực smallpart nhớ tất cả các dữ liệu từ data, mặc dù nó chỉ chứa chuỗi con.

Khi tôi đã thay đổi mã vào:

this.smallpart = data.substring(12,18)+""; 

..the vấn đề đã được giải quyết! Bây giờ ứng dụng của tôi sử dụng rất ít bộ nhớ ngay bây giờ!

Làm cách nào có thể? Bất cứ ai có thể giải thích điều này? Tôi nghĩ rằng this.smallpart tiếp tục tham chiếu tới dữ liệu, nhưng tại sao?

CẬP NHẬT: Làm cách nào để xóa chuỗi lớn? Sẽ dữ liệu = new String (data.substring (0,100)) làm điều này?

+0

Đọc thêm về ý định cuối cùng của bạn bên dưới: Chuỗi đầu tiên xuất phát từ đâu? Nếu đọc từ một tập tin hoặc cơ sở dữ liệu CLOB hoặc một cái gì đó sau đó chỉ đọc những gì bạn cần trong khi phân tích cú pháp sẽ được tối ưu tất cả các cách xung quanh. – PSpeed

+4

Tuyệt vời ... Tôi đang làm việc trong java hơn 4 đến 5 năm, vẫn còn điều này là mới đối với tôi :). cảm ơn cho bro thông tin. – Parth

+1

Có một sự tinh tế để sử dụng 'chuỗi mới (Chuỗi)'; xem http://stackoverflow.com/a/390854/8946. –

Trả lời

155

Làm như sau:

data.substring(x, y) + "" 

tạo ra một mới (nhỏ hơn) đối tượng String, và ném đi những tham chiếu đến chuỗi tạo ra bởi substring(), vì thế cho phép thu gom rác thải về điều này.

Điều quan trọng cần nhận ra là substring() cho một cửa sổ lên một hiện tại Chuỗi - hay đúng hơn, mảng ký tự nằm bên dưới Chuỗi ban đầu. Do đó nó sẽ tiêu thụ cùng một bộ nhớ như chuỗi gốc. Điều này có thể thuận lợi trong một số trường hợp, nhưng có vấn đề nếu bạn muốn nhận được một chuỗi con và vứt bỏ chuỗi gốc (như bạn đã tìm thấy).

Hãy xem substring() method trong nguồn chuỗi JDK để biết thêm thông tin.

EDIT: Để trả lời câu hỏi bổ sung của bạn, việc xây dựng một Chuỗi mới từ chuỗi con sẽ giảm mức tiêu thụ bộ nhớ của bạn, cung cấp bạn bin bất kỳ tham chiếu nào đến Chuỗi gốc.

LƯU Ý (tháng 1 năm 2013). Hành vi trên đã thay đổi in Java 7u6. Mô hình cân bằng không còn được sử dụng và substring() sẽ hoạt động như bạn mong đợi.

+88

Đó là một trong số ít trường hợp hàm tạo 'String (String)' (tức là hàm tạo String lấy String làm đầu vào) có ích: 'new String (data.substring (x, y))' thực hiện điều tương tự như bổ sung '" "', nhưng nó làm cho ý định hơi rõ ràng hơn. –

+3

chỉ để chính xác, chuỗi con sử dụng thuộc tính 'value' của chuỗi gốc. Tôi nghĩ đó là lý do tại sao tài liệu tham khảo được giữ lại. –

+0

@Bishiboosh - vâng, đúng vậy. Tôi không muốn vạch trần những đặc điểm của việc thực hiện, nhưng đó chính xác là những gì đang xảy ra. –

17

Khi bạn sử dụng substring, nó không thực sự tạo chuỗi mới. Nó vẫn đề cập đến chuỗi ban đầu của bạn, với một giới hạn bù đắp và kích thước.

Vì vậy, để cho phép chuỗi gốc của bạn được thu thập, bạn cần phải tạo một chuỗi mới (sử dụng new String hoặc những gì bạn đã có).

5

Trong chuỗi Java là các đối tượng imutable và một khi chuỗi được tạo, nó vẫn còn trên bộ nhớ cho đến khi nó được làm sạch bởi bộ thu gom rác (và việc dọn dẹp này không phải là thứ bạn có thể làm).

Khi bạn gọi phương thức chuỗi con, Java không tạo chuỗi mới trully, nhưng chỉ lưu trữ một loạt các ký tự bên trong chuỗi gốc.

Vì vậy, khi bạn đã tạo một chuỗi mới với mã này:

this.smallpart = data.substring(12, 18) + ""; 

bạn thực sự tạo ra một chuỗi mới khi bạn nối kết quả với chuỗi rỗng. Đó là lý do tại sao.

27

Nếu bạn nhìn vào nguồn gốc của substring(int, int), bạn sẽ thấy rằng nó sẽ trả về:

new String(offset + beginIndex, endIndex - beginIndex, value); 

nơi value là bản gốc char[]. Vì vậy, bạn nhận được một Chuỗi mới nhưng với cùng một số cơ bản char[].

Khi bạn thực hiện, data.substring() + "", bạn nhận được một Chuỗi mới với mới cơ bản char[].

Trên thực tế, trường hợp sử dụng của bạn là tình hình duy nhất mà bạn nên sử dụng String(String) constructor:

String tiny = new String(huge.substring(12,18)); 
+1

Có một sự tinh tế để sử dụng 'chuỗi mới (Chuỗi)'; xem http://stackoverflow.com/a/390854/8946. –

5

Tôi nghĩ this.smallpart giữ tham khảo đối với dữ liệu, nhưng tại sao?

Vì chuỗi Java bao gồm mảng char, độ lệch bắt đầu và độ dài (và mã băm được lưu trong bộ nhớ cache). Một số hoạt động String như substring() tạo đối tượng String mới chia sẻ mảng char ban đầu và chỉ có các trường bù trừ và/hoặc độ dài khác nhau. Điều này làm việc vì mảng char của một String không bao giờ được sửa đổi khi nó đã được tạo.

Điều này có thể tiết kiệm bộ nhớ khi nhiều chất nền đề cập đến cùng một chuỗi cơ bản mà không cần sao chép các phần chồng chéo. Như bạn đã nhận thấy, trong một số trường hợp, nó có thể giữ dữ liệu không cần thiết nữa từ việc thu gom rác thải.

"đúng" cách để sửa lỗi này là các nhà xây dựng new String(String), tức là

this.smallpart = new String(data.substring(12,18)); 

BTW, giải pháp tốt nhất tổng thể sẽ là để tránh việc Strings rất lớn ở nơi đầu tiên, và xử lý bất kỳ đầu vào nhỏ hơn khối, một vài KB tại một thời điểm.

+0

Có một sự tinh tế để sử dụng 'chuỗi mới (Chuỗi)'; xem http://stackoverflow.com/a/390854/8946. –

3

Như ghi nhận bởi jwz in 1997:

Nếu bạn có một chuỗi rất lớn, kéo ra một chuỗi con() của nó, giữ cho chuỗi con và cho phép các chuỗi thời gian để trở thành rác (hay nói cách khác, chuỗi con có tuổi thọ dài hơn) các byte cơ bản của chuỗi lớn không bao giờ biến mất.

2

Chỉ cần để tổng hợp, nếu bạn tạo nhiều chuỗi con từ một số nhỏ các chuỗi lớn, sau đó sử dụng

String subtring = string.substring(5,23) 

Vì bạn chỉ sử dụng không gian để lưu trữ các chuỗi lớn, nhưng nếu bạn là chiết xuất một số ít các chuỗi nhỏ, từ các chuỗi bị mất lớn, sau đó

Sẽ giữ cho bộ nhớ của bạn sử dụng được, vì các chuỗi lớn có thể được khai hoang khi không cần nữa.

Bạn gọi new String là lời nhắc hữu ích rằng bạn thực sự nhận được chuỗi mới, thay vì tham chiếu đến chuỗi gốc.

+0

Có một sự tinh tế để sử dụng 'chuỗi mới (Chuỗi)'; xem http://stackoverflow.com/a/390854/8946. –

2

Thứ nhất, gọi java.lang.String.substring tạo cửa sổ mới trên bản gốc String với việc sử dụng của bù đắp và thời gian thay vì sao chép một phần quan trọng của mảng tiềm ẩn.

Nếu chúng ta có một cái nhìn sâu hơn về các phương pháp substring chúng ta sẽ thấy một chuỗi constructor gọi String(int, int, char[]) và đi qua nó toàn char[] đại diện cho chuỗi. Điều đó có nghĩa là chuỗi phụ sẽ chiếm nhiều bộ nhớ như chuỗi gốc.

Ok, nhưng tại sao + "" kết quả là cần ít bộ nhớ hơn không có bộ nhớ ??

Thực hiện + trên strings được thực hiện qua cuộc gọi phương thức StringBuilder.append. Nhìn vào việc thực hiện phương pháp này trong lớp AbstractStringBuilder sẽ cho chúng ta biết rằng nó cuối cùng làm arraycopy với phần chúng ta thực sự cần (các substring).

Bất kỳ giải pháp nào khác ??

this.smallpart = new String(data.substring(12,18)); 
this.smallpart = data.substring(12,18).intern(); 
0

Phụ thêm "" thành một chuỗi sẽ đôi khi tiết kiệm bộ nhớ.

Giả sử tôi có một chuỗi lớn chứa toàn bộ cuốn sách, một triệu ký tự.

Sau đó, tôi tạo 20 chuỗi chứa các chương của cuốn sách dưới dạng bản chất.

Sau đó, tôi tạo 1000 chuỗi chứa tất cả các đoạn văn.

Sau đó, tôi tạo 10.000 chuỗi chứa tất cả các câu.

Sau đó, tôi tạo 100.000 chuỗi chứa tất cả các từ.

Tôi vẫn chỉ sử dụng 1.000.000 ký tự. Nếu bạn thêm "" vào từng chương, đoạn, câu và từ, bạn sử dụng 5.000.000 ký tự.Tất nhiên nó hoàn toàn khác nếu bạn chỉ trích xuất một từ duy nhất từ ​​toàn bộ cuốn sách, và toàn bộ cuốn sách có thể là rác được thu thập nhưng không phải vì một từ đó giữ một tham chiếu đến nó.

Và nó lại khác nếu bạn có chuỗi một triệu ký tự và xóa các tab và dấu cách ở cả hai đầu, cho biết 10 cuộc gọi để tạo chuỗi con. Cách Java hoạt động hoặc làm việc tránh việc sao chép hàng triệu ký tự mỗi lần. Có sự thỏa hiệp, và nó là tốt nếu bạn biết những thỏa hiệp là gì.

Các vấn đề liên quan