Lỗi java.lang.OutOfMemoryError: vượt quá giới hạn chi phí của GC


804

Tôi nhận được thông báo lỗi này khi tôi thực hiện các bài kiểm tra JUnit của mình:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Tôi biết an OutOfMemoryErrorlà gì , nhưng giới hạn trên không có nghĩa là gì? Làm sao tôi có thể giải quyết việc này?


16
Điều này nghe có vẻ rất thú vị. Tôi thích nếu ai đó có thể đăng một số mã tạo ra điều này.
Buhb

1
Tôi chỉ đơn giản tìm thấy vấn đề, dẫn đến việc sử dụng bộ nhớ quá nhiều, gần đến giới hạn của heap. Một giải pháp đơn giản có thể chỉ đơn giản là cung cấp thêm bộ nhớ Heap cho Công cụ Java (-Xmx) nhưng điều này chỉ giúp, nếu ứng dụng cần chính xác nhiều bộ nhớ, như giới hạn heap trước đó đã được đặt.
Mnementh

1
@Mnementh tôi đã đưa ra câu trả lời ở đây, hãy kiểm tra xem nó có giúp stackoverflow.com/questions/11091516/ không
lulu

16
@SimonKuang Lưu ý rằng có nhiều OutOfMemoryErrorkịch bản tăng heap không phải là giải pháp hợp lệ: hết luồng gốc và hết perm perm (tách biệt với heap) là hai ví dụ. Hãy cẩn thận về việc đưa ra tuyên bố quá rộng về OutOfMemoryErrors; có một loạt những thứ bất ngờ có thể gây ra chúng.
Tim

3
Làm thế nào bạn giải quyết vấn đề ??
Thorsten Niehues

Câu trả lời:


763

Thông báo này có nghĩa là vì một số lý do, trình thu gom rác chiếm quá nhiều thời gian (theo mặc định là 98% toàn bộ thời gian CPU của quy trình) và phục hồi rất ít bộ nhớ trong mỗi lần chạy (theo mặc định là 2% của heap).

Điều này có nghĩa là chương trình của bạn ngừng thực hiện bất kỳ tiến trình nào và bận rộn chỉ chạy bộ sưu tập rác mọi lúc.

Để ngăn ứng dụng của bạn giảm thời gian CPU mà không làm gì hết, JVM sẽ ném cái này Errorđể bạn có cơ hội chẩn đoán vấn đề.

Các trường hợp hiếm hoi mà tôi đã thấy điều này xảy ra là khi một số mã đang tạo ra hàng tấn các đối tượng tạm thời và hàng tấn các đối tượng được tham chiếu yếu trong một môi trường rất hạn chế bộ nhớ.

Hãy xem hướng dẫn điều chỉnh Java GC, có sẵn cho các phiên bản Java khác nhau và có các phần về vấn đề cụ thể này:


9
Sẽ là chính xác để tóm tắt câu trả lời của bạn như sau: "Nó giống như một lỗi 'Out of Java Heap space'. Cung cấp thêm bộ nhớ với -Xmx." ?
Tim Cooper

58
@Tim: Không, điều đó sẽ không chính xác. Trong khi cung cấp cho nó nhiều bộ nhớ hơn có thể làm giảm vấn đề, bạn cũng nên xem mã của mình và xem tại sao nó tạo ra lượng rác đó và tại sao mã của bạn lướt qua ngay dưới dấu "hết bộ nhớ". Nó thường là một dấu hiệu của mã bị hỏng.
Joachim Sauer

8
Cảm ơn, có vẻ như Oracle không thực sự tốt trong việc di chuyển dữ liệu, họ đã phá vỡ liên kết.
Joachim Sauer

151
Bạn đã cho tôi tại "Cảm ơn, có vẻ như Oracle không thực sự tốt như vậy"
Rob Grant

3
@Guus: nếu nhiều ứng dụng chạy trong cùng một JVM, thì có, chúng có thể dễ dàng ảnh hưởng lẫn nhau. Sẽ rất khó để nói ai đang làm sai. Tách các ứng dụng thành các JVM riêng biệt có thể là giải pháp dễ nhất.
Joachim Sauer

215

Trích dẫn từ bài viết của Oracle "Java SE 6 HotSpot [tm] Điều chỉnh bộ sưu tập rác máy ảo" :

Quá nhiều thời gian của GC và OutOfMemoryError

Người thu gom song song sẽ ném OutOfMemoryError nếu dành quá nhiều thời gian cho việc thu gom rác: nếu hơn 98% tổng thời gian dành cho việc thu gom rác và ít hơn 2% đống được thu hồi, OutOfMemoryError sẽ bị ném. Tính năng này được thiết kế để ngăn các ứng dụng chạy trong một khoảng thời gian dài trong khi thực hiện ít hoặc không tiến triển vì heap quá nhỏ. Nếu cần thiết, tính năng này có thể bị vô hiệu hóa bằng cách thêm tùy chọn -XX:-UseGCOverheadLimitvào dòng lệnh.

EDIT: có vẻ như ai đó có thể gõ nhanh hơn tôi :)


87
"Bạn có thể tắt cái này ..." nhưng OP rất có thể không nên làm điều này.
Stephen C

2
Bạn có thể cho tôi biết sự khác biệt giữa "-XX" và "-Xmx" không? Tôi cũng có thể tắt nó bằng tùy chọn "-Xmx".
Susheel Javadi

19
Trả lời một nhận xét rất cũ ở đây, nhưng ... @Bart Khi -XX:bắt đầu một số tùy chọn dòng lệnh là một cờ các loại cho biết tùy chọn này đặc biệt cao và không ổn định (có thể thay đổi mà không cần thông báo trong các phiên bản trong tương lai). Trong mọi trường hợp, -XX:-UseGCOverheadLimitcờ yêu cầu VM vô hiệu hóa kiểm tra giới hạn trên không của GC (thực ra là "tắt nó đi"), trong khi -Xmxlệnh của bạn chỉ tăng heap. Trong trường hợp thứ hai, kiểm tra chi phí hoạt động của GC vẫn đang chạy , có vẻ như một đống lớn hơn đã giải quyết các vấn đề đập của GC trong trường hợp của bạn (điều này không phải lúc nào cũng có ích).
Andrzej Doyle

1
Trong ứng dụng của tôi (đọc một tệp Excel lớn trong Talend), điều này không hoạt động và từ lời giải thích của người dùng khác, tôi hiểu tại sao. Điều này chỉ vô hiệu hóa lỗi nhưng vấn đề vẫn tồn tại và ứng dụng của bạn sẽ chỉ dành phần lớn thời gian để xử lý GC. Máy chủ của chúng tôi có rất nhiều RAM nên tôi đã sử dụng các đề xuất của Vitalii để tăng kích thước heap.
RobbZ

Cuối cùng bạn sẽ gặp lỗi này nếu ứng dụng của bạn quá nhiều dữ liệu, xóa bộ nhớ và tránh rò rỉ dữ liệu là cách tốt nhất - nhưng cần một chút thời gian.
Pievis

89

Nếu bạn chắc chắn không có rò rỉ bộ nhớ trong chương trình của mình, hãy thử:

  1. Tăng kích thước heap, ví dụ -Xmx1g.
  2. Cho phép bộ thu tạm dừng thấp đồng thời -XX:+UseConcMarkSweepGC.
  3. Tái sử dụng các đối tượng hiện có khi có thể để lưu một số bộ nhớ.

Nếu cần thiết, kiểm tra giới hạn có thể được tắt bằng cách thêm tùy chọn -XX:-UseGCOverheadLimitvào dòng lệnh.


9
Tôi không đồng ý với lời khuyên thứ ba. Tái sử dụng các đối tượng hiện có không lưu bộ nhớ (không rò rỉ các đối tượng cũ lưu bộ nhớ :-) Ngoài ra, "tái sử dụng đối tượng hiện tại" là một cách để giảm áp lực GC. Nhưng đó không phải là một ý tưởng hay: với GC hiện đại, chúng ta nên tránh các tình huống trong đó các vật thể cũ giữ vật mới bởi vì nó có thể phá vỡ một số giả định địa phương ...
mcoolive

@mcoolive: Để biết ví dụ có phần khó hiểu, hãy xem các bình luận để trả lời stackoverflow.com/a/5640498/4178262 bên dưới; việc tạo Listđối tượng bên trong vòng lặp khiến cho GC được gọi 39 lần thay vì 22 lần.
Mark Stewart

45

Nó thường là mã. Đây là một ví dụ đơn giản:

import java.util.*;

public class GarbageCollector {

    public static void main(String... args) {

        System.out.printf("Testing...%n");
        List<Double> list = new ArrayList<Double>();
        for (int outer = 0; outer < 10000; outer++) {

            // list = new ArrayList<Double>(10000); // BAD
            // list = new ArrayList<Double>(); // WORSE
            list.clear(); // BETTER

            for (int inner = 0; inner < 10000; inner++) {
                list.add(Math.random());
            }

            if (outer % 1000 == 0) {
                System.out.printf("Outer loop at %d%n", outer);
            }

        }
        System.out.printf("Done.%n");
    }
}

Sử dụng Java 1.6.0_24-b07 trên Windows 7 32 bit.

java -Xloggc:gc.log GarbageCollector

Sau đó nhìn vào gc.log

  • Kích hoạt 444 lần bằng phương pháp BAD
  • Kích hoạt 666 lần bằng phương pháp WORSE
  • Kích hoạt 354 lần bằng phương pháp TỐT HƠN

Hiện đã được cấp, đây không phải là thử nghiệm tốt nhất hoặc thiết kế tốt nhất nhưng khi gặp tình huống bạn không có lựa chọn nào khác ngoài việc thực hiện một vòng lặp như vậy hoặc khi xử lý mã hiện có hoạt động kém, chọn sử dụng lại các đối tượng thay vì tạo các đối tượng mới có thể giảm số lần người thu gom rác cản trở ...


12
Vui lòng làm rõ: Khi bạn nói "Đã kích hoạt n lần", điều đó có nghĩa là một GC thông thường đã xảy ra n lần hoặc lỗi "vượt quá giới hạn quá mức của GC" được báo cáo bởi OP đã xảy ra n lần?
Jon Schneider

Tôi đã thử nghiệm ngay bây giờ bằng cách sử dụng java 1.8.0_91 và không bao giờ gặp lỗi / ngoại lệ và "Triggered n lần" là từ việc đếm số lượng dòng trong gc.logtệp. Các thử nghiệm của tôi cho thấy tổng thể ít hơn nhiều lần, nhưng ít lần "Triggers" nhất cho TỐT HƠN, và bây giờ, BAD "tệ hơn" so với WORST bây giờ. Số lượng của tôi: BAD: 26, WORSE: 22, TỐT HƠN 21.
Mark Stewart

Tôi chỉ cần thêm một "WORST_YET" sửa đổi mà tôi xác định List<Double> listtrong vòng ngoài thay vì trước khi các vòng ngoài, và gây ra 39 bộ sưu tập rác.
Mark Stewart

36

Nguyên nhân gây ra lỗi theo Nền tảng Java [8], Hướng dẫn khắc phục sự cố phiên bản tiêu chuẩn : (đã nhấn mạnh và ngắt dòng)

[...] "Vượt quá giới hạn chi phí GC" chỉ ra rằng trình thu gom rác đang chạy mọi lúc và chương trình Java đang tiến triển rất chậm.

Sau khi thu gom rác, nếu quy trình Java dành hơn 98% thời gian để thực hiện việc thu gom rác và nếu nó đang phục hồi ít hơn 2% đống và đã thực hiện đến 5 (liên tục thời gian biên dịch) rác liên tiếp bộ sưu tập, sau đó a java.lang.OutOfMemoryErrorđược ném. [...]

  1. Tăng kích thước heap nếu heap hiện tại không đủ.
  2. Nếu bạn vẫn gặp lỗi này sau khi tăng bộ nhớ heap, hãy sử dụng các công cụ định hình bộ nhớ như MAT (công cụ phân tích bộ nhớ), Visual VM, v.v. và khắc phục rò rỉ bộ nhớ.
  3. Nâng cấp phiên bản JDK lên phiên bản mới nhất (1.8.x) hoặc ít nhất 1.7.x và sử dụng thuật toán G1GC. . Mục tiêu thông lượng cho G1 GC là 90 phần trăm thời gian ứng dụng và 10 phần trăm thời gian thu gom rác
  4. Ngoài việc thiết lập bộ nhớ heap với - Xms1g -Xmx2g, hãy thử

    -XX:+UseG1GC -XX:G1HeapRegionSize=n -XX:MaxGCPauseMillis=m  
    -XX:ParallelGCThreads=n -XX:ConcGCThreads=n

Hãy xem một số câu hỏi liên quan hơn về G1GC


29

Chỉ cần tăng kích thước heap lên một chút bằng cách đặt tùy chọn này trong

Chạy → Chạy Cấu hình → Đối số → Đối số VM

-Xms1024M -Xmx2048M

Xms - cho giới hạn tối thiểu

Xmx - cho giới hạn tối đa


2
Các ứng dụng Android không có argumentstab ... chúng ta nên làm gì để đạt được điều này?
Blaze Tama

3
Câu trả lời cho công cụ này là gì? Đó không phải là một câu hỏi Eclipse.
Michael Piefel

3
Không có "giới hạn tối thiểu". -Xms là kích thước ban đầu.
Diego Queiroz

1
Tối đa giới hạn tối đa có thể được đặt là gì ??
JPerk

14

Đối với tôi, các bước sau đây đã làm việc:

  1. Mở eclipse.initập tin
  2. Thay đổi

    -Xms40m
    -Xmx512m

    đến

    -Xms512m
    -Xmx1024m
  3. Khởi động lại Eclipse

Xem tại đây


cách đơn giản nhất để khắc phục vấn đề này. Cảm ơn :)
Hamza

1
tập tin eclipse.ini trong jdev?
Abhinaba Basu

vấn đề chưa được giải quyết ngay cả khi cấu hình đã được thay đổi thành này.
zionpi

1
OP đã không hỏi một câu hỏi Eclipse.
Michael Piefel

1
"Câu trả lời" này không trả lời câu hỏi trên.
Freitags

13

thử cái này

mở build.gradletập tin

  android {
        dexOptions {
           javaMaxHeapSize = "4g"
        }
   }

Hoạt động tuyệt vời cho các mô phỏng. Bất kỳ ý tưởng làm thế nào điều này ảnh hưởng đến các thiết bị thực sự? tức là đây là một ý tưởng tốt hay nó chỉ che giấu vấn đề? Cảm ơn.
Joshua Pinter

11

Sau đây làm việc cho tôi. Chỉ cần thêm đoạn mã sau:

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
dexOptions {
        javaMaxHeapSize "4g"
    }
}

Có, khi sử dụng Gradle :)
Alex

4
Làm thế nào bạn thậm chí có thể nghĩ rằng đây là một giải pháp cho câu hỏi của anh ấy nói chung ? Bạn đặt kích thước heap của mình thành 4g, hoàn toàn tùy ý trong cấu hình phân lớp cho facepalm Android .
Julian L.

7

tăng javaMaxHeapsize trong tệp build.gradle (Module: app) của bạn

dexOptions {
    javaMaxHeapSize "1g"
}

đến (Thêm dòng này trong lớp)

 dexOptions {
        javaMaxHeapSize "4g"
    }

3

Mô tả kích thước heap Java (xms, xmx, xmn)

-Xms size in bytes

Example : java -Xms32m

Đặt kích thước ban đầu của heap Java. Kích thước mặc định là 2097152 (2MB). Các giá trị phải là bội số của và lớn hơn 1024 byte (1KB). (Cờ -server tăng kích thước mặc định lên 32M.)

-Xmn size in bytes

Example : java -Xmx2m

Đặt kích thước heap Java ban đầu cho thế hệ Eden. Giá trị mặc định là 640K. (Cờ -server tăng kích thước mặc định lên 2M.)

-Xmx size in bytes

Example : java -Xmx2048m

Đặt kích thước tối đa mà heap Java có thể phát triển. Kích thước mặc định là 64M. (Cờ -server tăng kích thước mặc định lên 128M.) Giới hạn heap tối đa là khoảng 2 GB (2048MB).

Định dạng đối số bộ nhớ Java (xms, xmx, xmn)

Khi đặt kích thước heap Java, bạn nên chỉ định đối số bộ nhớ của mình bằng cách sử dụng một trong các chữ cái m m hoặc mÊM cho MB, hoặc khăn gặt hoặc giẻ giẻ cho GB. Cài đặt của bạn sẽ không hoạt động nếu bạn chỉ định phiên bản MB MB hoặc hoặc GB GB. Đối số hợp lệ trông như thế này:

-Xms64m hoặc -Xms64M -Xmx1g hoặc -Xmx1G Cũng có thể sử dụng 2048MB để chỉ định 2GB Ngoài ra, hãy đảm bảo bạn chỉ sử dụng toàn bộ số khi chỉ định đối số của mình. Sử dụng -Xmx512m là một tùy chọn hợp lệ, nhưng -Xmx0.5g sẽ gây ra lỗi.

Tài liệu tham khảo này có thể hữu ích cho một ai đó.


2

Bạn cũng có thể tăng cấp phát bộ nhớ và kích thước heap bằng cách thêm phần này vào gradle.propertiestệp của mình :

org.gradle.jvmargs=-Xmx2048M -XX\:MaxHeapSize\=32g

Nó không phải là 2048M và 32g, làm cho nó lớn như bạn muốn.


2

Đã giải quyết:
Chỉ cần thêm
org.gradle.jvmargs=-Xmx1024m
vào
gradle.properties
và nếu nó không tồn tại, hãy tạo nó.


0

Tôi đang làm việc trong Android Studio và gặp phải lỗi này khi cố gắng tạo APK đã ký để phát hành. Tôi đã có thể xây dựng và kiểm tra APK gỡ lỗi mà không gặp vấn đề gì, nhưng ngay khi tôi muốn xây dựng APK phát hành, quá trình xây dựng sẽ chạy trong vài phút và cuối cùng chấm dứt với "Lỗi java.lang.OutOfMemoryError: GC vượt quá giới hạn ". Tôi đã tăng kích thước heap cho cả trình biên dịch VM và Android DEX, nhưng vấn đề vẫn tồn tại. Cuối cùng, sau nhiều giờ và cốc cà phê, hóa ra vấn đề nằm ở tệp 'build.gradle' cấp độ ứng dụng của tôi - Tôi có tham số 'minifyEnables' cho loại bản dựng phát hành được đặt thành 'false', do đó chạy các công cụ Proguard trên mã chưa qua quy trình thu nhỏ mã (xem https://developer.android.). Tôi đã thay đổi tham số 'minifyEnables' thành 'true' và bản dựng phát hành được thực hiện như một giấc mơ :)

Nói tóm lại, tôi đã phải thay đổi tệp 'build.gradle' cấp độ ứng dụng của mình từ: // ...

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

đến

    //...

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

0

Để tăng kích thước heap trong IntelliJ IDEA, hãy làm theo các hướng dẫn sau. Nó làm việc cho tôi.

Đối với người dùng Windows,

Đi đến vị trí nơi IDE được cài đặt và tìm kiếm theo sau.

idea64.exe.vmoptions

Chỉnh sửa tập tin và thêm vào như sau.

-Xms512m
-Xmx2024m
-XX:MaxPermSize=700m
-XX:ReservedCodeCacheSize=480m

Thế là xong !!


0

bạn có thể thử thay đổi cài đặt máy chủ bằng cách tham khảo hình ảnh này và tăng kích thước bộ nhớ để xử lý các thay đổi được tô sáng bằng màu vàng

bạn cũng có thể thay đổi heap java bằng cách mở cmd-> set _java_opts -Xmx2g
2g (2gigabyte) tùy theo mức độ phức tạp của chương trình của bạn

cố gắng sử dụng các biến tạm thời và biến tạm thời ít hơn

nhập mô tả hình ảnh ở đây


-1

Bạn cần tăng kích thước bộ nhớ trong Jdeveloper, hãy truy cập setDomainEnv.cmd .

set WLS_HOME=%WL_HOME%\server    
set XMS_SUN_64BIT=**256**
set XMS_SUN_32BIT=**256**
set XMX_SUN_64BIT=**3072**
set XMX_SUN_32BIT=**3072**
set XMS_JROCKIT_64BIT=**256**
set XMS_JROCKIT_32BIT=**256**
set XMX_JROCKIT_64BIT=**1024**
set XMX_JROCKIT_32BIT=**1024**

if "%JAVA_VENDOR%"=="Sun" (
    set WLS_MEM_ARGS_64BIT=**-Xms256m -Xmx512m**
    set WLS_MEM_ARGS_32BIT=**-Xms256m -Xmx512m**
) else (
    set WLS_MEM_ARGS_64BIT=**-Xms512m -Xmx512m**
    set WLS_MEM_ARGS_32BIT=**-Xms512m -Xmx512m**
)

set MEM_PERM_SIZE_64BIT=-XX:PermSize=**256m**
set MEM_PERM_SIZE_32BIT=-XX:PermSize=**256m**

if "%JAVA_USE_64BIT%"=="true" (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT%
) else (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT%
)

set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=**1024m**
set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=**1024m**

4
Các cài đặt này chỉ dành riêng cho IDE cục bộ của bạn. Điều này sẽ không làm việc cho môi trường Prod.
Phong

-1

Trong Netbeans, có thể hữu ích khi thiết kế kích thước heap tối đa. Đi đến Chạy => Đặt cấu hình dự án => Tùy chỉnh . Trong cửa sổ Run of popped của nó, đi đến Tùy chọn VM , điền vào -Xms2048m -Xmx2048m. Nó có thể giải quyết vấn đề kích thước đống.


-1

Khởi động lại MacBook của tôi đã khắc phục vấn đề này cho tôi.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.