2010-07-25 31 views
6

Tôi đã gặp phải sự cố tôi không thể tìm thấy giải pháp. Tôi đang sử dụng một HashSet để lưu trữ các giá trị. Các giá trị tôi lưu trữ là loại chu kỳ tùy chỉnh mà tôi đã ghi đè mã băm và bằng như sau để đảm bảo hiệu suất chậm không được mã hóa bằng phương pháp hascode hoặc các phương thức tương đương Ngoài ra tôi đã đặt công suất ban đầu của hashset thành 10.000.000HashSet. hiệu suất chậm trong bộ lớn

@Override 
public int hashCode() { 
final int prime = 31; 
int result = 1; 
result = prime * result + (int) (cycleId^(cycleId >>> 32)); 
return result; 
} 

@Override 
public boolean equals(Object obj) { 
if (this == obj) 
return true; 
if (obj == null) 
return false; 
if (getClass() != obj.getClass()) 
return false; 
Cycle other = (Cycle) obj; 
if (cycleId != other.cycleId) 
return false; 
return true; 
} 

Sau 1.500.000 giá trị đầu tiên khi tôi cố thêm giá trị mới (với phương pháp thêm của lớp HashSet), chương trình rất chậm. Cuối cùng tôi sẽ có java ra khỏi ngoại lệ bộ nhớ (ngoại lệ trong chủ đề "Thread-0" java.lang.OutOfMemoryError: Java heap không gian) trước khi các giá trị được lưu trữ đạt 1.600.000

IDE i sử dụng là Eclipse. Vì vậy, bước tiếp theo là tăng kích thước heap JVM từ giá trị mặc định lên 1 giga (sử dụng dấu phẩy Xmx1000M và Xms1000M) Giờ đây elipse bắt đầu với bộ nhớ gấp 10 lần (tôi có thể thấy điều đó ở góc dưới bên phải. kích thước bộ nhớ và bộ nhớ được sử dụng được hiển thị) nhưng một lần nữa tôi có cùng một hiệu suất "chậm" và cùng một lỗi bộ nhớ TRONG GIÁ TRỊ CÙNG như trước (sau 1.500.000 và trước 1.600.000) là rất kỳ quặc.

Có ai có ý tưởng đó có phải là vấn đề không?

Cảm ơn bạn trước

+1

CycleId chính xác là gì? Nếu nó là một ID như trong danh tính và do đó duy nhất cho các chu kỳ sau đó chỉ cần trả về cycleId như hashcode. Nếu nó không phải là một số nguyên, sau đó lấy hashCode của loại đó là gì. Nếu nó là 64 bit và ID bắt đầu từ 0 (với phân phối đồng đều hoặc nhiều nhất trong bit 32 bit thấp hơn) thì hãy chuyển nó thành int. –

+0

@lasseespeholt, tại sao? Sau đó, hashcode sẽ chỉ phụ thuộc vào 32 bit thấp hơn của lâu! Sử dụng * tất cả * các bit là con đường để đi. Hãy tưởng tượng loại thảm họa nào sẽ xảy ra nếu String.hashCode() chỉ sử dụng vài ký tự cuối cùng để tạo 32 hashCode! –

+1

Bạn đã lược tả chương trình của mình để xác minh rằng đó là 'HashSet' đang làm chậm mọi thứ? –

Trả lời

10

Bạn không muốn để tăng đống JVM cho Eclipse, bạn muốn thiết lập nó cho chương trình của bạn.

Đến Run> Run Configurations (hoặc Debug Cấu hình) và thiết lập VM Tùy chọn đó.

0

Có thể máy tính của bạn không có đủ bộ nhớ, do đó nó phải chuyển sang đĩa.

2

Kích thước bộ nhớ có sẵn cho ứng dụng bạn khởi động từ Eclipse nên được cấu hình từ trình đơn Run. Hãy thử:

Run -> Run Configurations -> Arguments -> VM Arguments -> -Xmx1000M

Lý do tại sao chương trình của bạn chậm là Bộ thu gom rác - nó bắt đầu mỗi khi bộ nhớ sắp hết hạn.

+0

tôi đã đặt VM thành 1G và chỉ sử dụng ít hơn 100M. chương trình hoạt động tương tự với máy ảo 50 M hoặc VM của 1G. Strange đủ tôi có một bộ nhớ đầy lỗi khi thiết lập có khoảng 1500.000 và 1.600.000 yếu tố indepedently bao nhiêu memroy tôi đã thiết lập. Bộ nhớ kích thước heap trong Eclipse được hiển thị ở góc dưới cùng bên phải vì vậy tôi đã kiểm tra kỹ xem các lệnh Xmx và Xms có hoạt động chính xác hay không .. – Pitelk

+0

Cảm ơn Vitalii. Tôi đã kiểm tra jconsol và hóa ra là bạn đúng 100%. Chương trình sử dụng tất cả bộ nhớ trong khi nhật thực chỉ hiển thị 50MB trong số 1000MB. Ngoài ra Andreas là đúng .. tôi phải tuyên bố rõ ràng với cấu hình ăn trưa mà tôi muốn chương trình sử dụng tất cả các không gian heap độc lập với thực tế tôi đã bắt đầu nhật thực với 1000M đống. Cảm ơn sự giúp đỡ của bạn – Pitelk

1

Nếu bạn muốn tăng bộ nhớ, chương trình của bạn có thể sử dụng nó sẽ không giúp tăng kích thước heap của Eclipse. Bạn phải đặt tham số vào thông số vm của cấu hình khởi chạy của chương trình.

+0

Cảm ơn bạn Andreas! bạn có quyền về điều đó .. tôi phải tuyên bố rõ ràng với cấu hình bữa trưa mà tôi muốn chương trình sử dụng tất cả các không gian heap độc lập với thực tế tôi đã bắt đầu nhật thực với 1000M đống. Cảm ơn sự giúp đỡ của bạn – Pitelk

2

Bạn đã thử nghiệm triển khai phương pháp hashCode của mình chưa? nó luôn trả về 31, cho bất kỳ giá trị nào của circleId. Không lạ gì khi HashMap của bạn hoạt động chậm, nó có hiệu suất tuyến tính.

+0

Nop, bạn có thể hiểu sai việc triển khai, điều đó là tốt. –

+0

@Dimitris Andreou: Tôi đã đọc chính xác. Nó ** luôn luôn ** trả về 31 vì '(X^(X >>> 32))' luôn trả về '0' cho ints. – Roman

+0

Rõ ràng là một thời gian dài. (Lưu ý các diễn viên rõ ràng để int). –

1

JVM ném 'hết bộ nhớ' KHÔNG dựa trên bộ nhớ khả dụng. Nó được ném khi thời gian dành cho việc thu gom rác thải quá nhiều. check this. Chi tiết triển khai chính xác khác nhau dựa trên JVM và việc triển khai bộ thu gom rác.

Tăng bộ nhớ sẽ không giúp ích trong trường hợp này. Bạn có thể phải chọn cách tiếp cận khác.

+0

Có thể bạn đã đúng. Nhưng vấn đề là tôi có dung lượng trống và dung lượng trống được hiển thị ở phía dưới bên phải của nhật thực để tôi có thể thấy khi nào bộ thu gom rác đang chạy khi nó giải phóng rất nhiều tài nguyên bộ nhớ. Ngoài ra bộ nhớ được sử dụng khi lỗi bật lên là 150M trong số 1000M. Như xa như tôi biết thu gom rác không chạy cho đến khi bộ nhớ nhiều hơn là cần thiết, nơi đây đây không phải là trường hợp. Cảm ơn câu trả lời của bạn – Pitelk

+0

hóa ra là bộ nhớ ảo eclise không giống với bộ nhớ ảo mà chương trình của tôi sử dụng .. Tôi phải đặt bộ nhớ ảo của eclipse thành 1G nhưng sau đó tôi phải làm tương tự trong cấu hình chạy. Ngoài ra kích thước heap hiển thị đề cập đến kích thước heap của eclipse được sử dụng và không phải ứng dụng của tôi. Vì vậy, trong hậu quả .. chương trình của tôi sử dụng thực sự tất cả bộ nhớ. – Pitelk

4

Không đủ bộ nhớ heap (tăng bộ nhớ thông qua -Xmx, ví dụ: -Xmx512m). Khi bộ nhớ miễn phí đi rất thấp, sau đó nhiều, nhiều thời gian được chi tiêu bởi các nhà sưu tập rác mà điên cuồng quét đống cho các đối tượng unreachable.

Mã hashCode() của bạn là tốt, các điểm bổ sung để sử dụng tất cả các bit của chiều dài cycleId dài.

Chỉnh sửa. Bây giờ tôi thấy bạn đã tăng bộ nhớ, và không giúp đỡ.Trước hết, bạn có chắc chắn rằng bạn đã quản lý để tăng bộ nhớ? Bạn có thể kiểm tra điều này bằng jconsole, kết nối với ứng dụng của bạn và xem kích thước của nó.

Để có giải thích thay thế cần được xác minh, có bất kỳ mẫu cụ thể nào trong số cycleId của bạn có thể làm cho việc triển khai hashCode() này xấu không? Giống như, 32 bit thứ tự cao của nó chủ yếu tương tự như 32 bit thứ tự thấp. (Phải, đúng).

Nhưng không. Ngay cả khi đó là trường hợp, bạn sẽ thấy một sự suy giảm dần về hiệu suất, không phải là giảm mạnh tại một điểm cụ thể (và bạn có được một OutOfMemoryError và hoạt động gc điên cuồng). Vì vậy, dự đoán tốt nhất của tôi vẫn là vấn đề về bộ nhớ. Bạn không tăng kích thước heap như bạn nghĩ, hoặc có một số bộ nhớ lấy mã khác tại một số điểm. (Bạn có thể sử dụng một công cụ như VisualVM để cấu hình này, và có được một đống đống khi OOME, và xem những gì các đối tượng nó chứa).

Edit2 Tôi đã in đậm phần chính xác ở trên.

+0

cũng .. phương pháp hachcode được tự động tạo ra bởi nhật thực ...Vì vậy, thêm điểm để nhật thực :) Theo như Xmx tôi đã inreased nó, nhưng kỳ lạ tôi có cùng một vấn đề. Điều kỳ lạ là ngay cả khi tôi đã tăng bộ nhớ heap, các chương trình bắt đầu nhận được chậm trong cùng một lượng dữ liệu như trước .... – Pitelk

+0

Xem câu trả lời cập nhật của tôi, chúng tôi đã gõ cùng một lúc. –

+0

(Cũng xem phần còn lại của các câu trả lời cho thấy bạn không tăng đống ứng dụng của bạn, nhưng thực tế nhật thực.) –

0

Bạn đang khởi tạo HashSet của mình như thế nào? Bạn cần phải nhận thức được mô hình tăng trưởng của nó. Với mỗi thao tác add, nó sẽ kiểm tra xem nó có gần hết dung lượng hay không. Nếu nó đạt đến một điểm nhất định (được xác định bởi 'yếu tố tải' của nó), nó thực hiện một hoạt động thay đổi kích thước có thể tốn kém. Từ javadoc (của HashMap - bộ sưu tập mà sao HashSet):

As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.

+0

công suất ban đầu được đặt là 10.000.000 và các chương trình bắt đầu chậm ở mức 1.500.000 do đó điều này không phải là trường hợp. Cảm ơn bạn đã anwser – Pitelk

0

Tôi khá thất vọng về số lượng câu trả lời cho OP để tăng kích thước heap trong ứng dụng của mình. Đó không phải là một giải pháp - đó là một bản vá nhanh và bẩn, sẽ không giải quyết bất kỳ vấn đề cơ bản nào.

tôi thấy trình bày này cực kỳ thông tin: http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf

Chủ yếu là các trang liệt kê các kích thước byte tối thiểu của mỗi khi empty--

ArrayList: 40 or 48 
LinkedList: 48 
HashMap: 56 or 120 
HashSet: 72 or 136 

Chỉ ra rằng một HashSet thực chất là một HashMap, và (ngược lại) chiếm nhiều bộ nhớ hơn mặc dù chỉ giữ các giá trị thay vì các cặp khóa-giá trị.

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