2009-12-31 32 views
16

Câu hỏi nói lên tất cả nhưng đây là một ví dụ:Làm thế nào để xử lý realloc khi nó không thành công do bộ nhớ?

typedef struct mutable_t{ 
    int count, max; 
    void **data; 
} mutable_t; 


void pushMutable(mutable_t *m, void *object) 
{ 
    if(m->count == m->max){ 
     m->max *= 2; 
     m->data = realloc(m->data, m->max * sizeof(void*)); 
    } 
    // how to handle oom?? 
    m->data[m->count++] = object; 
} 

Làm thế nào tôi có thể xử lý chạy ra khỏi bộ nhớ và không NULL ra tất cả các dữ liệu của tôi?

chỉnh sửa - giả sử có điều gì đó có thể được thực hiện, ví dụ: giải phóng bộ nhớ ở đâu đó hoặc ít nhất là nói với người dùng "bạn không thể làm điều đó - bạn đã hết bộ nhớ". Lý tưởng nhất là tôi muốn để lại những gì được phân bổ ở đó.

+1

phụ thuộc nhiều vào ứng dụng ... nhưng có một điều chắc chắn, OOM là khá quan trọng. – jldupont

+0

liên quan: http://stackoverflow.com/questions/1941323/always-check-malloced-memory – jldupont

+0

Chỉ cần thêm vào một vài câu trả lời ở đây, một ý tưởng về cách xử lý một thất bại 'realloc()' (trong trường hợp của bạn) sẽ làm 'm-> max/= 4; m-> max * = 3; 'và thử gọi lại' realloc() 'để xem liệu chúng ta có thể vẫn còn thêm một vài byte nữa không. Bạn thậm chí có thể thử một vài lần với kích thước liên tục nhỏ hơn, nhưng tại một số điểm nó sẽ không có giá trị nó. –

Trả lời

18

Kỹ thuật chuẩn là giới thiệu một biến mới để giữ lợi tức từ realloc. Sau đó bạn chỉ ghi đè biến đầu vào của bạn nếu nó thành công:

tmp = realloc(orig, newsize); 
if (tmp == NULL) 
{ 
    // could not realloc, but orig still valid 
} 
else 
{ 
    orig = tmp; 
} 
+0

Vì vậy, nó không được thiết lập để NULL cho đến khi chuyển nhượng? Đó là tốt để biết. –

+6

Và sau đó là gì? Bạn đã không cố gắng tăng kích thước của mảng của bạn cho vui, bạn thực sự cần thiết cho một lý do. – Blindy

+2

@Blindy - không hoạt động. Tùy thuộc vào logic của ứng dụng, nó sẽ được vào nó để quyết định làm thế nào để phục hồi (có lẽ đây là một máy chủ và nó sẽ thất bại một yêu cầu nhưng vẫn tiếp tục chạy các yêu cầu khác). Nhưng điều này trông giống như mã thư viện cấp thấp mà không nên ép buộc một chính sách ngoài bộ nhớ trên ứng dụng. –

7

Đây là một chút của một nút chủ đề nóng như về cơ bản có 2 trường phái tư tưởng về chủ đề

  1. Dò tìm các oom, và có chức năng trả lại một mã lỗi.
  2. Dò tìm các oom và sụp đổ quá trình của bạn càng nhanh càng tốt

Cá nhân tôi đang ở trại # 2. Mong đợi cho các loại ứng dụng rất đặc biệt, OOM là thời kỳ gây tử vong. Đúng, mã được viết hoàn hảo có thể xử lý OOM nhưng rất ít người hiểu cách viết mã an toàn khi không có bộ nhớ. Thậm chí ít bận tâm hơn để thực sự làm điều đó bởi vì nó gần như không bao giờ đáng để nỗ lực.

Tôi không thích chuyển mã lỗi sang chức năng gọi cho OOM vì nó tương đương với việc nói cho người gọi "Tôi đã thất bại và bạn không thể làm gì với nó". Thay vào đó, tôi thích sụp đổ nhanh hơn, do đó kết xuất kết quả là càng nhiều càng tốt.

+1

Những điều có khả năng _can_ được thực hiện về lỗi OOM. Không có nhiều, nhưng có thể trong một số trường hợp. (Trong hầu hết các ứng dụng, phải có một trình bao bọc xung quanh 'malloc()' và 'realloc()' chỉ thoát với thông báo lỗi về lỗi bộ nhớ, nhưng chúng không làm điều đó cho vài ứng dụng có giải pháp tốt hơn). –

+0

@ Chris, chắc chắn đúng và một số sản phẩm (máy chủ SQL ví dụ) là khá tốt ở đó. Tuy nhiên những sản phẩm đó là ngoại lệ hiếm. Bắt nó đúng đòi hỏi một số tiền tuyệt vời của kỷ luật, thực thi và sự hiểu biết. Rất nhiều để mọi người hiếm khi cố gắng làm đúng. – JaredPar

+0

@JaredPar, vì vậy bạn về cơ bản nói vì hầu hết mọi người không nhận được lỗi xử lý đúng, bạn thậm chí không nên bận tâm chăm sóc lỗi và thay vào đó hãy để ứng dụng bị lỗi và ghi, có thể làm hỏng dữ liệu của người dùng? Vấn đề là OOM xảy ra khi chạy trên máy của người dùng. Bạn không có quyền kiểm soát kích thước bộ nhớ trong các máy này và trên không gian HD cho tệp hoán đổi. Sau đó, thêm rò rỉ bộ nhớ vào nó ... Plus, nó là khá dễ dàng để kiểm tra rằng ứng dụng của bạn có thể xử lý nó. Sử dụng một wrapper cho malloc/realloc trả về NULL một cách ngẫu nhiên. – Secure

2

Đó hoàn toàn là vấn đề của bạn! Dưới đây là một số tiêu chí:

  • Bạn đã yêu cầu bộ nhớ đó vì lý do. Nếu nó không có sẵn, công việc của chương trình của bạn có phải là doomed hoặc nó có thể tiếp tục làm việc không? Nếu trước đây, bạn muốn chấm dứt chương trình của bạn với một thông báo lỗi; nếu không, bạn có thể hiển thị thông báo lỗi bằng cách nào đó và tiếp tục.

  • Có khả năng giao dịch thời gian cho không gian không? Bạn có thể trả lời bất kỳ thao tác nào bạn đã thử bằng thuật toán sử dụng ít bộ nhớ hơn không? Điều đó nghe có vẻ như rất nhiều công việc nhưng sẽ có hiệu lực là một khả năng để tiếp tục hoạt động của chương trình của bạn mặc dù không có đủ bộ nhớ ban đầu.

  • Chương trình của bạn có tiếp tục bị hạn chế mà không có dữ liệu này và không đủ bộ nhớ không? Nếu vậy, bạn nên chấm dứt bằng một thông báo lỗi. Nó tốt hơn nhiều để giết chương trình của bạn hơn là mù quáng tiếp tục xử lý dữ liệu không chính xác.

3

Nguyên tắc đầu tiên mà bạn shoud làm theo khi làm việc với realloc không phải là để gán giá trị trở lại của realloc để con trỏ tương tự mà bạn truyền cho nó. Điều này

m->data = realloc(m->data, m->max * sizeof(void*)); 

là xấu. Nếu realloc không thành công, nó sẽ trả về con trỏ rỗng, nhưng nó không giải quyết được bộ nhớ cũ.Mã trên sẽ vô hiệu hóa m->data của bạn trong khi khối bộ nhớ cũ trước đây được chỉ định bởi m->data rất có thể sẽ trở thành rò rỉ bộ nhớ (nếu bạn không có tham chiếu khác đến nó).

Giá trị trả về của realloc nên được lưu trữ trong một con trỏ riêng biệt đầu tiên

void **new_data; 
... 
new_data = realloc(m->data, m->max * sizeof(void*)); 

Sau đó, bạn có thể kiểm tra cho sự thành công/thất bại và thay đổi giá trị của m->data trong trường hợp thành công

if (new_data != NULL) 
    m->data = new_data; 
else 
    /* whatever */; 
1

Có cũng là một lỗi tinh tế khác có thể đến từ realloc. Việc rò rỉ bộ nhớ đến từ con trỏ NULL trả về là khá nổi tiếng (nhưng khá hiếm khi vấp ngã). Tôi đã có trong một chương trình của tôi một tai nạn một lần trong một thời gian mà đến từ một cuộc gọi realloc. Tôi đã có một cấu trúc động điều chỉnh kích thước của nó tự động với một giá trị tương tự như sau:

m->data = realloc(m->data, m->max * sizeof(void*)); 

Lỗi tôi đã thực hiện là không kiểm tra m-> max == 0, giải phóng vùng bộ nhớ. Và được làm từ con trỏ dữ liệu m-> cũ của tôi.

Tôi biết đó là một chút ngoài chủ đề nhưng đây là vấn đề thực sự duy nhất tôi từng có với realloc.

+0

Điều thú vị tôi vừa phát hiện ra (tức là trong năm 2016) là stdlib mà tôi đã sử dụng tại thời điểm đó không tuân thủ đúng tiêu chuẩn, vì 'realloc()' được yêu cầu trả về 'NULL' trong trường hợp có cuộc gọi với 0 chiều dài. Điều này sẽ không kích hoạt lỗi ở nơi đầu tiên. Hấp dẫn, bởi vì tôi nhớ rất rõ lỗi đó, đã xảy ra vào khoảng năm 2004 trên một máy Solaris rất cũ (đã được sử dụng trong thời gian đó). –

2
  1. Tìm hiểu cách khung ứng dụng xử lý OOM. Nhiều người sẽ không xử lý OOM một cách đơn giản. Hầu hết thời gian một khuôn khổ sẽ không hoạt động đúng trong điều kiện không có RAM miễn trừ khi nó nói rất rõ ràng và rõ ràng ở đâu đó mà nó sẽ. Nếu khung công tác sẽ không xử lý OOM và được đa luồng (nhiều người hiện nay), thì OOM sẽ là kết thúc của chương trình cho quá trình trong nhiều trường hợp. Ngay cả khi nó không đa luồng, nó vẫn có thể gần như sụp đổ. Cho dù bạn thoát khỏi quá trình hoặc khung làm việc có thể là một điểm tranh luận; một lối thoát ngay lập tức có thể đoán trước có thể tốt hơn một chút so với một vụ tai nạn ở một số điểm bán ngẫu nhiên trong tương lai gần.

  2. Nếu bạn đang sử dụng một nhóm bộ nhớ phụ chuyên dụng riêng biệt (nghĩa là không phải bình thường của bạn) cho một nhóm hoạt động được xác định rõ ràng chỉ bị hạn chế trong sử dụng bộ nhớ của OOM (tức là hoạt động hiện tại được cuộn trở lại hoặc hủy bỏ sạch trên OOM cho nhóm bộ nhớ phụ, không phải toàn bộ quá trình hoặc nhóm bộ nhớ chính) và tiểu nhóm đó cũng không được khung ứng dụng sử dụng hoặc nếu khung của bạn và WHOLE của phần còn lại của ứng dụng được thiết kế để duy trì trạng thái có ý nghĩa và tiếp tục hoạt động trong các điều kiện không có RAM (hiếm nhưng không nghe thấy ở chế độ hạt nhân và một số loại lập trình hệ thống), bạn có thể trả lại mã lỗi thay vì làm hỏng quy trình.

  3. Lý tưởng nhất là phần lớn các cấp phát bộ nhớ (hoặc thậm chí lý tưởng hơn tất cả phân bổ) cho một mảnh chế biến nên được phân bổ càng sớm càng tốt trong chế biến, tốt nhất là trước khi nó bắt đầu đúng cách, để giảm thiểu các vấn đề về dữ liệu mất toàn vẹn và/hoặc số lượng mã hóa rollback được yêu cầu nếu nó không thành công. Trong thực tế rất nhiều thời gian, để tiết kiệm chi phí và thời gian cho các dự án, để bảo toàn các ứng dụng toàn vẹn dữ liệu dựa trên các giao dịch cơ sở dữ liệu và yêu cầu người dùng/người hỗ trợ phát hiện sự cố GUI (hoặc sự cố máy chủ) và khởi động lại ứng dụng khi hết lỗi bộ nhớ xảy ra, thay vì được viết để đối phó với và rollback trên bất kỳ và tất cả hàng ngàn tình huống OOM tiềm năng theo những cách tốt nhất có thể. Sau đó, các nỗ lực tập trung vào việc cố gắng hạn chế sự tiếp xúc của ứng dụng với tình huống quá tải, có thể bao gồm xác nhận bổ sung và giới hạn về kích thước dữ liệu và kết nối đồng thời và truy vấn.

  4. Thậm chí nếu bạn kiểm tra số lượng bộ nhớ được báo cáo là có sẵn, thường mã khác có thể phân bổ hoặc bộ nhớ trống như bạn làm, thay đổi cơ sở để kiểm tra bộ nhớ và có thể dẫn đến OOM. Vì vậy, việc kiểm tra RAM miễn phí trước khi bạn cấp phát thường không phải là giải pháp đáng tin cậy cho vấn đề đảm bảo ứng dụng của bạn hoạt động trong giới hạn RAM có sẵn và duy trì tính toàn vẹn dữ liệu đủ thời gian để thỏa mãn người dùng.

  5. Tình huống tốt nhất để biết số lượng bộ nhớ mà ứng dụng của bạn yêu cầu trong mọi trường hợp có thể, bao gồm mọi chi phí khuôn khổ và giữ con số đó trong số lượng RAM có sẵn cho ứng dụng của bạn, nhưng hệ thống thường phức tạp với các phụ thuộc bên ngoài dictating kích thước dữ liệu để đạt được điều này có thể không thực tế.

Các thử nghiệm axit tất nhiên là được bạn đáp ứng người dùng đầy đủ thông qua cao lên theo thời gian, và dữ liệu không thường xuyên tham nhũng, mất mát hoặc bị treo. Trong một số trường hợp, một ứng dụng có quá trình theo dõi để khởi động lại nó nếu nó bị treo rất hữu ích.

Liên quan đến realloc:

Kiểm tra giá trị trả về từ realloc - đặt giá trị đó vào biến tạm thời. Chỉ quan tâm nếu nó là NULL nếu kích thước mới được yêu cầu là> 0. Trong trường hợp khác đặt nó trong biến phi tạm thời của bạn:

ví dụ

void* temp = realloc(m->data, m->max * sizeof(void*)); 
    if (m->max!=0&&temp==NULL) { /* crash or return error */ } 
    m->data =(void**)temp; 

EDIT

Changed "hầu hết trường hợp" thành "nhiều trường hợp" trong (1).

Tôi nhận ra rằng bạn đã giả định rằng "có thể làm được gì đó" nếu bộ nhớ không thể được cấp phát. Nhưng quản lý bộ nhớ là một sự xem xét rất toàn cầu (!).

8

Chiến lược về việc cần làm khi realloc() không phụ thuộc vào ứng dụng của bạn. Câu hỏi là quá chung chung để được trả lời cho tất cả các trường hợp có thể xảy ra.

Một số lưu ý khác:

Không bao giờ làm:

a = realloc(a, size); 

Nếu realloc() thất bại, bạn bị mất con trỏ ban đầu, và realloc() không free() bộ nhớ ban đầu, vì vậy bạn sẽ có được một sự rò rỉ bộ nhớ. Thay vào đó, làm:

tmp = realloc(a, size); 
if (tmp) 
    a = tmp; 
else 
    /* handle error */ 

điểm thứ hai tôi muốn làm là nhỏ và có thể không phải là quan trọng, nhưng nó rất tốt để biết về nó anyway: tăng bộ nhớ được phân bổ theo hệ f là tốt. Giả sử bạn malloc()n byte trước tiên. Sau đó, bạn cần nhiều bộ nhớ hơn, vì vậy bạn hãy realloc() với kích thước n × f. Sau đó, bạn cần thêm bộ nhớ, vì vậy bạn cần n × f byte. Nếu bạn muốn realloc() để sử dụng không gian từ hai khối bộ nhớ trước đó, bạn muốn đảm bảo rằng n × f ≤ n + n × f.Giải phương trình này, chúng tôi nhận được f ≤ (sqrt (5) +1)/2 = 1.618 (số Golden ratio). Tôi sử dụng hệ số 1.5 hầu hết các lần.

+0

bạn có bất kỳ tài liệu nào khác về thuật toán phân bổ bộ nhớ không? – Jori

1

Tôi đã gặp vấn đề. Cấu hình là hệ điều hành: win7 (64); IDE: vs2013; Debug (Win32).
Khi realloc của tôi trả về null do bộ nhớ in.I có hai giải pháp cho nó:

1.Thay đổi thuộc tính của dự án, để kích hoạt ĐỊA CHỈ LỚN.
2.Thay đổi nền tảng giải pháp của tôi từ Win32 thành x64.

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