2012-07-05 22 views
54

Tôi có một thường trình chạy các truy vấn khác nhau đối với cơ sở dữ liệu SQLite nhiều lần trong một giây. Sau một thời gian, tôi sẽ gặp lỗiSQLite Cơ sở dữ liệu Android Phân bổ cửa sổ con trỏ 2048 kb không thành công

"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = " xuất hiện trong LogCat.

Tôi đã sử dụng bộ nhớ đăng nhập ứng dụng và thực sự khi mức sử dụng đạt đến giới hạn nhất định, tôi nhận được lỗi này, ngụ ý rằng nó hết. Trực giác của tôi nói với tôi rằng công cụ cơ sở dữ liệu đang tạo bộ đệm MỚI (CursorWindow) mỗi lần tôi chạy truy vấn và mặc dù tôi đánh dấu con trỏ .close(), không phải bộ thu gom rác cũng không phải SQLiteDatabase.releaseMemory() đủ nhanh để giải phóng bộ nhớ. Tôi nghĩ rằng các giải pháp có thể nằm trong "buộc" cơ sở dữ liệu để luôn luôn viết vào cùng một bộ đệm, và không tạo ra những cái mới, nhưng tôi đã không thể tìm thấy một cách để làm điều này. Tôi đã thử instantiating CursorWindow của riêng tôi, và cố gắng thiết lập nó và SQLiteCursor không có kết quả.

¿Bất kỳ ý tưởng nào?

EDIT: tái yêu cầu mã ví dụ từ @GrahamBorland:

public static CursorWindow cursorWindow = new CursorWindow("cursorWindow"); 
public static SQLiteCursor sqlCursor; 
public static void getItemsVisibleArea(GeoPoint mapCenter, int latSpan, int lonSpan) { 
query = "SELECT * FROM Items"; //would be more complex in real code 
sqlCursor = (SQLiteCursor)db.rawQuery(query, null); 
sqlCursor.setWindow(cursorWindow); 
} 

Lý tưởng nhất là tôi muốn để có thể .setWindow() trước khi đưa ra một truy vấn mới, và có các dữ liệu đưa vào cùng CursorWindow mọi tôi nhận được dữ liệu mới .

+0

Tôi không biết vấn đề là gì .. :) nhưng tôi sử dụng để làm lớp singleton SQLiteOpenHelper. Vì vậy, tôi không bao giờ tìm thấy bất kỳ vấn đề như thế này. –

+0

Không, tôi không sử dụng SQLiteOpenHelper, tôi tạo một lớp DataAccess tĩnh có chứa một SQLiteDatabase. Điều này làm việc tốt và tôi nghi ngờ vấn đề là có. Vấn đề có nhiều hơn để làm với các thư viện SQLite tạo ra một container mới để đặt kết quả của mỗi truy vấn mới, thay vì sử dụng cùng một container hơn và hơn nữa. Và mặc dù tôi có thể đóng con trỏ, tốc độ làm sạch GC sẽ chậm hơn tốc độ tạo vùng chứa mới, do đó tạo ra bộ nhớ. – alex

+0

Bạn có thể hiển thị một số mã của bạn, đặc biệt là những gì bạn đã cố gắng thực hiện với việc thiết lập 'CursorWindow' của riêng bạn? –

Trả lời

88

Thông thường nguyên nhân gây ra lỗi này là con trỏ không bị đóng. Hãy chắc chắn rằng bạn đóng tất cả các con trỏ sau khi sử dụng chúng (ngay cả trong trường hợp có lỗi).

Cursor cursor = null; 
try { 
    cursor = db.query(... 
    // do some work with the cursor here. 
} finally { 
    // this gets called even if there is an exception somewhere above 
    if(cursor != null) 
     cursor.close(); 
} 
+0

Bạn thực sự có thể chỉ đơn giản là ví dụ để tránh kiểm tra null và null. – aij

+2

Cũng giống như con trỏ tệp mở - LUÔN xử lý việc đóng phần cuối cùng để đảm bảo mã của bạn tồn tại một cách rõ ràng. – slott

11

Tôi vừa mới gặp sự cố này - và câu trả lời được đề xuất không đóng con trỏ trong khi hợp lệ, không phải cách tôi cố định. Vấn đề của tôi là đóng cơ sở dữ liệu khi SQLite đang cố gắng phục hồi lại con trỏ của nó. Tôi sẽ mở cơ sở dữ liệu, truy vấn cơ sở dữ liệu để đưa con trỏ đến tập dữ liệu, đóng cơ sở dữ liệu và lặp lại con trỏ. Tôi nhận thấy bất cứ khi nào tôi nhấn một bản ghi nào đó trong con trỏ đó, ứng dụng của tôi sẽ bị lỗi với cùng lỗi này trong OP.

Tôi giả định rằng để con trỏ truy cập các bản ghi nhất định, nó cần phải truy vấn lại cơ sở dữ liệu và nếu nó được đóng, nó sẽ ném lỗi này. Tôi đã sửa nó bằng cách không đóng cơ sở dữ liệu cho đến khi tôi hoàn thành tất cả công việc tôi cần.

62

Nếu bạn phải khai thác một lượng đáng kể mã SQL, bạn có thể tăng tốc độ gỡ lỗi bằng cách đặt đoạn mã sau vào MainActivity để bật StrictMode. Nếu các đối tượng cơ sở dữ liệu bị rò rỉ được phát hiện thì ứng dụng của bạn giờ đây sẽ bị lỗi với thông tin nhật ký làm nổi bật chính xác nơi rò rỉ của bạn. Điều này đã giúp tôi xác định vị trí một con trỏ lừa đảo chỉ trong vài phút.

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    if (BuildConfig.DEBUG) {  
     StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 
     .detectLeakedSqlLiteObjects() 
     .detectLeakedClosableObjects() 
     .penaltyLog() 
     .penaltyDeath() 
     .build()); 
    } 
    super.onCreate(savedInstanceState); 
    ... 
    ... 
+6

Thật tuyệt vời! Tôi đã thực hiện tiêu chuẩn này với 'if (BuildConfig.DEBUG) {...}' bên trong một khối 'tĩnh {...}' của lớp chính của tôi. –

+1

Cách tốt nhất và hiệu quả nhất để gỡ lỗi loại vấn đề mà tôi đã tìm thấy cho đến nay, nên là câu trả lời được chấp nhận! – androidseb

+0

Tuyệt vời! Nhưng trong bản phát hành sẽ làm việc mà không có StrictMode? – alfdev

0
public class CursorWindowFixer { 

    public static void fix() { 
    try { 
     Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); 
     field.setAccessible(true); 
     field.set(null, 102400 * 1024); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 
} 
+0

Bạn nên thêm một số nhận xét giải thích mã của bạn – sme

+0

Giới hạn Breakthrough CursorWindow chỉ có 2 megabyte –

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