Bộ nhớ bị rò rỉ càng ít byte càng tốt


79

Nhiệm vụ của bạn là viết mã sẽ rò rỉ ít nhất một byte bộ nhớ trong càng ít byte càng tốt. Bộ nhớ phải bị rò rỉ không chỉ được phân bổ .

Bộ nhớ bị rò rỉ là bộ nhớ mà chương trình phân bổ nhưng mất khả năng truy cập trước khi có thể giải phóng bộ nhớ đúng cách. Đối với hầu hết các ngôn ngữ cấp cao, bộ nhớ này phải được phân bổ trên heap.

Một ví dụ trong C ++ sẽ là chương trình sau:

int main(){new int;}

Điều này làm cho một new intđống trên mà không có một con trỏ đến nó. Bộ nhớ này bị rò rỉ ngay lập tức vì chúng tôi không có cách nào để truy cập nó.

Đây là bản tóm tắt rò rỉ từ Valgrind có thể trông như thế nào:

LEAK SUMMARY:
   definitely lost: 4 bytes in 1 blocks
   indirectly lost: 0 bytes in 0 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 0 bytes in 0 blocks
        suppressed: 0 bytes in 0 blocks

Nhiều ngôn ngữ có trình gỡ lỗi bộ nhớ (chẳng hạn như Valgrind ) nếu bạn có thể nên đưa đầu ra từ trình gỡ lỗi đó để xác nhận rằng bạn đã bị rò rỉ bộ nhớ.

Mục tiêu là để giảm thiểu số lượng byte trong nguồn của bạn.


2
Có lẽ bạn có thể có các phạm vi số lượng khác nhau bị rò rỉ và tùy thuộc vào mức độ rò rỉ của bạn, bạn mất x% số byte của mình
Christopher

11
@ChristopherPeart Đối với một người tôi không phải là người thích thưởng cho các thử thách và đối với hai người như bạn đã chỉ ra, rất dễ bị rò rỉ bộ nhớ không giới hạn.
Thuật sĩ lúa mì

1
Liên quan . Tuy nhiên, không phải là một bản sao vì hầu hết các câu trả lời cho câu hỏi đó tạo thành một cấu trúc có thể tiếp cận vô hạn trong bộ nhớ thay vì thực sự bị rò rỉ bộ nhớ.

2
ý tưởng là gì Rằng các mem không thể được giải thoát? Tôi đoán điều này sẽ yêu cầu thực thi riêng đối với các ngôn ngữ được thu thập rác hoặc khai thác lỗi.
akostadinov

7
Tôi thấy cách các ngôn ngữ được thiết kế để chơi golf thất bại thảm hại trên cái này ...
Kh40tiK

Câu trả lời:


89

Perl (5.22.2), 0 byte

Hãy thử trực tuyến!

Tôi biết có một số ngôn ngữ ngoài đó bị rò rỉ bộ nhớ trên một chương trình trống. Tôi đã mong đợi nó là một esolang, nhưng hóa ra nó làm perlrò rỉ bộ nhớ trên bất kỳ chương trình nào. (Tôi cho rằng điều này là có chủ ý, vì giải phóng bộ nhớ nếu bạn biết rằng bạn sẽ thoát dù sao cũng chỉ lãng phí thời gian; vì vậy, khuyến nghị phổ biến hiện nay là chỉ rò rỉ bất kỳ bộ nhớ còn lại nào khi bạn trong thói quen thoát chương trình của bạn .)

xác minh

$ echo -n | valgrind perl
snip
==18517== 
==18517== LEAK SUMMARY:
==18517==    definitely lost: 8,134 bytes in 15 blocks
==18517==    indirectly lost: 154,523 bytes in 713 blocks
==18517==      possibly lost: 0 bytes in 0 blocks
==18517==    still reachable: 0 bytes in 0 blocks
==18517==         suppressed: 0 bytes in 0 blocks
==18517== 
==18517== For counts of detected and suppressed errors, rerun with: -v
==18517== ERROR SUMMARY: 15 errors from 15 contexts (suppressed: 0 from 0)

16
Tôi thích câu trả lời của Unlambda, nhưng câu trả lời này là (IMHO) quá nhiều, vì rõ ràng chính trình thông dịch đã làm rò rỉ bộ nhớ, tức là tôi bị mất 'chắc chắn: 7,742 byte trong 14 khối' khi tôi chạy perl --versiontrên máy của mình , mặc dù nó không bao giờ được chạy bất kỳ chương trình nào cả.
zeppelin

11
@zeppelin: Đồng ý, nhưng theo quy tắc của chúng tôi, đó là việc triển khai xác định ngôn ngữ, do đó, nếu việc triển khai làm rò rỉ bộ nhớ, tất cả các chương trình trong bộ nhớ bị rò rỉ ngôn ngữ. Tôi không chắc chắn tôi đồng ý với quy tắc đó, nhưng tại thời điểm này, nó quá cố chấp để thực sự có thể thay đổi.

8
Điều này cũng hoạt động trong Node JS.
Dennis

6
Cảm giác này giống như một lỗ hổng tiêu chuẩn mới trong quá trình tạo ...
Michael Hampton

46
Cuối cùng là một kịch bản Perl mà tôi có thể hiểu.
dùng11153

66

C, 48 31 22 byte

Cảnh báo: Đừng chạy cái này quá nhiều lần.

Cảm ơn Dennis vì rất nhiều sự giúp đỡ / ý tưởng!

f(k){shmget(k,1,512);}

Điều này đi một bước xa hơn. shmgetcấp phát bộ nhớ chia sẻ không bị phân bổ khi chương trình kết thúc. Nó sử dụng một khóa để xác định bộ nhớ, vì vậy chúng tôi sử dụng một int chưa được khởi tạo. Đây là hành vi không xác định về mặt kỹ thuật, nhưng thực tế nó có nghĩa là chúng tôi sử dụng giá trị nằm ngay trên đỉnh của ngăn xếp khi được gọi. Điều này sẽ được viết trong lần tiếp theo khi bất cứ thứ gì được thêm vào ngăn xếp, vì vậy chúng tôi sẽ mất khóa.


Trường hợp duy nhất mà nó không hoạt động là nếu bạn có thể tìm ra những gì trên ngăn xếp trước đó. Để có thêm 19 byte, bạn có thể tránh được vấn đề này:

f(){srand(time(0));shmget(rand(),1,512);}

Hoặc, với 26 byte:

main(k){shmget(&k,1,512);}

Nhưng với cái này, bộ nhớ bị rò rỉ sau khi chương trình thoát ra. Trong khi chạy chương trình có quyền truy cập vào bộ nhớ trái với quy tắc, nhưng sau khi chương trình kết thúc, chúng ta mất quyền truy cập vào khóa và bộ nhớ vẫn được cấp phát. Điều này đòi hỏi ngẫu nhiên bố trí không gian địa chỉ (ASLR), nếu không &ksẽ luôn giống nhau. Ngày nay, ASLR thường được bật theo mặc định.


Xác minh:

Bạn có thể sử dụng ipcs -mđể xem những gì bộ nhớ chia sẻ tồn tại trên hệ thống của bạn. Tôi đã xóa các mục có sẵn cho rõ ràng:

$ cat leakMem.c 
f(k){shmget(k,1,512);}
int main(){f();}     
$ gcc leakMem.c -o leakMem
leakMem.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 f(k){shmget(k,1,512);}
 ^
leakMem.c: In function ‘f’:
leakMem.c:1:1: warning: type of ‘k’ defaults to ‘int’ [-Wimplicit-int]
leakMem.c:1:6: warning: implicit declaration of function ‘shmget’ [-Wimplicit-function-declaration]
 f(k){shmget(k,1,512);}
ppcg:ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      


$ ./leakMem 

$ ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

0x0000007b 3375157    Riley      0          1          0  

1
@AndrewSavinykh Về mặt lý thuyết, shmid có thể đã được lưu trữ trong một tệp và một chương trình có thể đính kèm vào đó trong tương lai. Đây là cách bộ nhớ chia sẻ unix hoạt động ...
tbodt

1
@AndrewSavinykh Bộ nhớ dùng chung về cơ bản trở thành tài nguyên mà HĐH có thể cung cấp cho các quy trình khác. Nó tương tự như một tệp sống trong RAM và bất kỳ quá trình nào biết tên (khóa) đều có quyền truy cập vào nó cho đến khi nó bị xóa. Tưởng tượng một quá trình tính toán một số và lưu nó vào bộ nhớ và thoát ra trước khi quá trình đọc dữ liệu kết nối với bộ nhớ dùng chung. Trong trường hợp này, nếu HĐH giải phóng bộ nhớ thì quá trình thứ hai không thể có được.
Riley

35
Cảm ơn vì đã đăng tải điều này. Tôi chỉ bảo vệ TIO chống rò rỉ bộ nhớ chia sẻ.
Dennis

4
@Dennis Đó là lý do tại sao tôi không đăng liên kết TIO. Tôi không biết nó có được bảo vệ hay không.
Riley

12
Tôi thích cách bạn sử dụng vấn đề từ ngữ để mô tả kịch bản trong đó chương trình rò rỉ ít bộ nhớ hơn dự định.
kasperd

40

Unlambda ( c-refcnt/unlambda), 1 byte

i

Hãy thử trực tuyến!

Đây thực sự là một thách thức về việc tìm kiếm một trình thông dịch có sẵn làm rò rỉ bộ nhớ trên các chương trình rất đơn giản. Trong trường hợp này, tôi đã sử dụng Unlambda. Có nhiều hơn một thông dịch viên chính thức của Unlambda, nhưngc-refcnt là một trong những trình thông dịch dễ xây dựng nhất và nó có thuộc tính hữu ích ở đây là nó rò rỉ bộ nhớ khi chương trình chạy thành công. Vì vậy, tất cả những gì tôi cần cung cấp ở đây là chương trình Unlambda hợp pháp đơn giản nhất có thể, không có op. (Lưu ý rằng chương trình trống không hoạt động ở đây; bộ nhớ vẫn có thể truy cập được tại thời điểm trình thông dịch gặp sự cố.)

xác minh

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
Snip snio
2017 / 02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' đã lưu [492894]
$ tar xf unlambda-2.0.0.tar.gz 
$ cd unlambda-2.0.0 / c-refcnt /
$ gcc unlambda.c
$ echo -ni | valgrind ./a.out / dev / stdin
Snip snio
== 3417 == LEAK TÓM TẮT:
== 3417 == chắc chắn bị mất: 40 byte trong 1 khối
== 3417 == Mất gián tiếp: 0 byte trong 0 khối
== 3417 == có thể bị mất: 0 byte trong 0 khối
== 3417 == vẫn có thể truy cập: 0 byte trong 0 khối
== 3417 == bị chặn: 0 byte trong 0 khối
== 3417 == 
== 3417 == Đối với số lỗi được phát hiện và loại bỏ, hãy chạy lại với: -v
== 3417 == TÓM TẮT LRI: 1 lỗi từ 1 bối cảnh (bị loại bỏ: 0 từ 0)

39

TI-Basic, 12 byte

While 1
Goto A
End
Lbl A
Pause 

"... Rò rỉ bộ nhớ là nơi bạn sử dụng Goto / Lbl trong vòng lặp hoặc Nếu có điều kiện (bất cứ thứ gì có lệnh End) để nhảy ra khỏi cấu trúc điều khiển đó trước khi đạt được lệnh End ..." (thêm)


7
Wow, tôi nghĩ rằng tôi nhớ điều này. Tôi liên tục nhảy ra khỏi các vòng lặp trong các chương trình cơ bản cũ của mình và nhận thấy TI-84 + của tôi ngày càng chậm hơn ...
Ray

2
Đúng, hầu hết chúng ta đều biết cảm giác đó;) @RayKoopa
Timtech

13
+1 cho Ti Cơ bản. Tôi đã dành hầu hết năm lớp 9 để lập trình những thứ đó.
markasoftware

Bạn có cần Pause ở cuối không? Bạn có thể lưu 2 byte.
kamoroso94

@ kamoroso94 Tôi nghĩ vậy, bởi vì "Nếu một chương trình kết thúc, việc rò rỉ sẽ bị xóa và sẽ không gây ra vấn đề gì nữa", vì vậy đó là để ngăn chặn chương trình kết thúc.
Timtech

32

Python <3.6.5, 23 byte

property([]).__init__()

property.__init__rò rỉ tài liệu tham khảo để ở tại khách sạn cũ fget, fset, fdel, và __doc__nếu bạn gọi nó trên một đã khởi tạo propertyví dụ. Đây là một lỗi, cuối cùng được báo cáo là một phần của vấn đề CPython 31787 và đã được sửa trong Python 3.6.5Python 3.7.0 . (Ngoài ra, vâng, property([])là một điều bạn có thể làm.)


Có một báo cáo lỗi đã được gửi?
mbomb007


27

C #, 34 byte

class L{~L(){for(;;)new L();}}

Đây giải pháp không yêu cầu Heap. Nó chỉ cần một công việc thực sự chăm chỉ ( Garbage Collector ).

Về cơ bản, nó biến GC thành kẻ thù của chính mình.

Giải trình

Bất cứ khi nào hàm hủy được gọi, Nó sẽ tạo ra các thể hiện mới của lớp tà ác này, miễn là hết thời gian chờ và bảo cho GC chỉ bỏ qua đối tượng đó mà không đợi cho hàm hủy kết thúc. Đến lúc đó, hàng ngàn trường hợp mới đã được tạo ra.

"Sự xấu xa" của điều này là, GC càng làm việc chăm chỉ, điều này sẽ càng nổ tung trên khuôn mặt của bạn.

Tuyên bố miễn trừ trách nhiệm : GC của bạn có thể thông minh hơn tôi. Các trường hợp khác trong chương trình có thể làm cho GC bỏ qua đối tượng đầu tiên hoặc hàm hủy của nó. Trong những trường hợp này, nó sẽ không nổ tung. Nhưng trong nhiều biến thể nó sẽ . Thêm một vài byte ở đây và có thể đảm bảo rò rỉ cho mọi trường hợp có thể. Vâng, ngoại trừ công tắc nguồn có thể.

Kiểm tra

Đây là một bộ thử nghiệm :

using System;
using System.Threading;
using System.Diagnostics;
class LeakTest {
    public static void Main() {
        SpawnLeakage();
        Console.WriteLine("{0}-: Objects may be freed now", DateTime.Now);
        // any managed object created in SpawbLeakage 
        //  is no longer accessible
        // The GC should take care of them

        // Now let's see
        MonitorGC();
    }
    public static void SpawnLeakage() {
        Console.WriteLine("{0}-: Creating 'leakage' object", DateTime.Now);
        L l = new L();
    }
    public static void MonitorGC() {
        while(true) {
            int top = Console.CursorTop;
            int left = Console.CursorLeft;
            Console.WriteLine(
                "{0}-: Total managed memory: {1} bytes",
                DateTime.Now,
                GC.GetTotalMemory(false)
            );
            Console.SetCursorPosition(left, top);
        }
    }
}

Đầu ra sau 10 phút:

2/19/2017 2:12:18 PM-: Creating 'leakage' object
2/19/2017 2:12:18 PM-: Objects may be freed now
2/19/2017 2:22:36 PM-: Total managed memory: 2684476624 bytes

Đó là 2 684 476 624 byte. Tổng WorkingSetquá trình là khoảng 4,8 GB

Câu trả lời này được lấy cảm hứng từ bài viết tuyệt vời của Eric Lippert: Khi mọi thứ bạn biết đều sai .


Điều này thật hấp dẫn. Có phải người thu gom rác "Quên" rằng một số thứ tồn tại và mất dấu vết của chúng vì điều này? Tôi không biết nhiều về c #. Ngoài ra bây giờ tôi tự hỏi, sự khác biệt giữa một quả bom và rò rỉ là gì? Tôi tưởng tượng một fiasco tương tự có thể được tạo ra bằng cách gọi một hàm tạo từ bên trong của hàm tạo hoặc có một hàm đệ quy vô hạn không bao giờ dừng, mặc dù về mặt kỹ thuật, hệ thống không bao giờ mất dấu vết của các tham chiếu đó, nó chỉ chạy ra ngoài không gian ...
don sáng

1
Một constructor trong một constructor sẽ gây ra tràn stack. Nhưng hàm hủy của một thể hiện được gọi trong một hệ thống phân cấp phẳng. Các GC thực sự không bao giờ mất theo dõi của các đối tượng. Chỉ cần bất cứ khi nào nó cố gắng tiêu diệt chúng, nó vô tình tạo ra các đối tượng mới. Mặt khác, mã người dùng không có quyền truy cập vào các đối tượng đã nói. Ngoài ra, sự không nhất quán được đề cập có thể phát sinh do GC có thể quyết định phá hủy một đối tượng mà không gọi hàm hủy của nó.
MrPaulch

Thử thách sẽ không hoàn thành nếu chỉ sử dụng class L{~L(){new L();}}? AFAIK for(;;)chỉ làm cho nó rò rỉ bộ nhớ nhanh hơn, phải không?
BgrWorker

1
Thật đáng buồn không. Vì đối với mỗi đối tượng bị phá hủy, chỉ có một thể hiện mới sẽ được tạo, sau đó một lần nữa không thể truy cập và được đánh dấu để hủy. Nói lại. Chỉ có một đối tượng sẽ chờ xử lý để tiêu hủy. Không tăng dân số.
MrPaulch

2
Không hẳn vậy. Cuối cùng, một quyết toán sẽ được bỏ qua. Các đối tượng tương ứng sẽ được ăn bất kể.
MrPaulch

26

C (gcc) , 15 byte

f(){malloc(1);}

xác minh

$ cat leak.c
f(){malloc(1);}
main(){f();}
$ gcc -g -o leak leak.c
leak.c: In function ‘f’:
leak.c:1:5: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
 f(){malloc(1);}
     ^
$ valgrind --leak-check=full ./leak
==32091== Memcheck, a memory error detector
==32091== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==32091== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==32091== Command: ./leak
==32091==
==32091==
==32091== HEAP SUMMARY:
==32091==     in use at exit: 1 bytes in 1 blocks
==32091==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==32091==
==32091== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==32091==    at 0x4C29110: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==32091==    by 0x40056A: f (leak.c:1)
==32091==    by 0x40057A: main (leak.c:2)
==32091==
==32091== LEAK SUMMARY:
==32091==    definitely lost: 1 bytes in 1 blocks
==32091==    indirectly lost: 0 bytes in 0 blocks
==32091==      possibly lost: 0 bytes in 0 blocks
==32091==    still reachable: 0 bytes in 0 blocks
==32091==         suppressed: 0 bytes in 0 blocks
==32091==
==32091== For counts of detected and suppressed errors, rerun with: -v
==32091== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

26

Javascript, 14 byte

Chơi gôn

setInterval(0)

Đăng ký một trình xử lý khoảng trống với độ trễ mặc định, loại bỏ id bộ định thời kết quả (làm cho nó không thể hủy bỏ).

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

Tôi đã sử dụng một khoảng thời gian không mặc định, để tạo ra vài triệu bộ định thời, để minh họa sự rò rỉ, khi sử dụng một khoảng thời gian mặc định ăn CPU như điên.


5
Haha Tôi thích việc bạn đã gõ 'Golfed', khiến tôi tò mò về phiên bản không được phép
Martijn

9
nó có thể trông như thế nàyif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka

3
Trên thực tế, điều này không thể hủy bỏ: ID (và thời gian chờ) ID được đánh số liên tục, do đó khá dễ dàng để hủy bỏ mọi thứ chỉ bằng cách gọi clearIntervalvới một ID tăng dần cho đến khi hết khoảng thời gian của bạn. Ví dụ:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118

5
@ user2428118 Như zeppelin nói, đây không phải là "hợp pháp" hơn là nói rò rỉ C / C ++ không phải là "thực" bởi vì bạn có thể bắt free()
bẻ

1
Ồ, không có nhiều thách thức trong đó JavaScript là một ứng cử viên thực sự ...
Jared Smith

19

Java, 10 byte

Cuối cùng, một câu trả lời cạnh tranh trong Java!

Chơi gôn

". "::trim

Đây là một tham chiếu phương thức (đối với hằng chuỗi), có thể được sử dụng như thế:

Supplier<String> r = ". "::trim

Một chuỗi ký tự ". "sẽ được tự động thêm vào nhóm chuỗi nội bộ toàn cầu , như được duy trì bởi java.lang.Stringlớp và khi chúng ta cắt xén nó, tham chiếu đến chuỗi đó không thể được sử dụng lại trong mã (trừ khi bạn khai báo lại chính xác cùng một chuỗi).

...

Một chuỗi các chuỗi, ban đầu trống, được duy trì riêng bởi Chuỗi lớp.

Tất cả các chuỗi ký tự và các biểu thức hằng có giá trị chuỗi được thực hiện. Chuỗi ký tự được định nghĩa trong phần 3.10.5 của Đặc tả ngôn ngữ Java ™.

...

https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#itern--

Bạn có thể biến điều này trong một rò rỉ bộ nhớ "cấp sản xuất", bằng cách thêm chuỗi vào chính nó và sau đó gọi phương thức intern () một cách rõ ràng, trong một vòng lặp.


2
Tôi đã xem xét điều này cho C # ... nhưng tôi không nghĩ nó có giá trị, vì như bạn nói bạn có thể truy cập vào bộ nhớ đó bằng cách bao gồm một chuỗi ký tự khác. Tôi cũng muốn biết những gì ("." + " ").intern()sẽ làm (nếu họ là đầu vào của người dùng hoặc w / e, vì vậy chúng tôi giảm giá tối ưu hóa trình biên dịch).
VisualMelon

1
Thật vậy, sự đồng thuận duy nhất là mỏng nhất, tôi chỉ chắc chắn về phía "mã nên biên dịch". Tôi vẫn không chắc chắn mình đã mua giải pháp này khi đưa ra từ ngữ cho câu hỏi (các chuỗi này không thể được giải phóng và chúng có thể được tìm thấy trong mã hoạt động bình thường mặc dù điều đó là không thể), nhưng tôi mời người khác tự đưa ra phán đoán
VisualMelon

3
Chuỗi đó thậm chí không thể truy cập được , hãy để một mình bị rò rỉ. Chúng ta có thể lấy nó bất cứ lúc nào bằng cách tạo một chuỗi bằng nhau. Nếu điều này được tính, bất kỳ biến toàn cục nào không được sử dụng (private static trong Java) sẽ bị rò rỉ. Đó không phải là cách rò rỉ bộ nhớ được xác định.
user2357112

3
@ user2357112 "... Chuỗi đó thậm chí không thể truy cập được ..." Điều đó chỉ có vẻ rõ ràng vì bạn thấy mã. Bây giờ hãy xem xét bạn đã tham chiếu phương thức X () này làm đối số cho mã của mình, bạn biết rằng nó phân bổ (và thực tập) một chuỗi ký tự bên trong, nhưng bạn không biết chính xác cái nào có thể là "." Hoặc "123" hoặc bất kỳ chuỗi nào khác có độ dài (nói chung) không xác định. Bạn có thể vui lòng chứng minh làm thế nào bạn vẫn có thể truy cập nó, hoặc sắp xếp lại mục trong nhóm "thực tập" mà nó chiếm không?
zeppelin

2
@ user2357112 Trên một máy có bộ nhớ hữu hạn, bạn có thể truy cập một giá trị được lưu trong bất kỳ phần bộ nhớ nào simply by guessing it correctly, nhưng điều đó không có nghĩa là những thứ như rò rỉ bộ nhớ không tồn tại. there's probably some way to use reflection to determine the string's contents toobạn có thể chứng minh điều này? (gợi ý, String.i INTERN () được triển khai trong mã gốc ).
zeppelin

17

Rust, 52 byte

extern{fn malloc(_:u8);}fn main(){unsafe{malloc(9)}}

Phân bổ một số byte với malloc hệ thống. Điều này giả định ABI sai là chấp nhận được.


Rust (về lý thuyết), 38 byte

fn main(){Box::into_raw(Box::new(1));}

Chúng tôi phân bổ bộ nhớ trên heap, trích xuất một con trỏ thô, và sau đó chỉ cần bỏ qua nó, rò rỉ nó một cách hiệu quả. ( Box::into_rawngắn hơn rồi std::mem::forget).

Tuy nhiên, Rust theo mặc định sử dụng jemalloc, valgrind không thể phát hiện bất kỳ rò rỉ nào . Chúng tôi có thể chuyển sang phân bổ hệ thống nhưng điều đó thêm 50 byte và yêu cầu hàng đêm. Cảm ơn rất nhiều cho sự an toàn bộ nhớ.


Đầu ra của chương trình đầu tiên:

==10228== Memcheck, a memory error detector
==10228== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10228== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==10228== Command: ./1
==10228== 
==10228== 
==10228== HEAP SUMMARY:
==10228==     in use at exit: 9 bytes in 1 blocks
==10228==   total heap usage: 7 allocs, 6 frees, 2,009 bytes allocated
==10228== 
==10228== LEAK SUMMARY:
==10228==    definitely lost: 9 bytes in 1 blocks
==10228==    indirectly lost: 0 bytes in 0 blocks
==10228==      possibly lost: 0 bytes in 0 blocks
==10228==    still reachable: 0 bytes in 0 blocks
==10228==         suppressed: 0 bytes in 0 blocks
==10228== Rerun with --leak-check=full to see details of leaked memory
==10228== 
==10228== For counts of detected and suppressed errors, rerun with: -v
==10228== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

tuyệt vời. những bài đăng như thế này đã dẫn tôi khám phá Rust trong năm qua, chắc chắn là một trong những ngôn ngữ thú vị nhất mà tôi đã cố gắng học.
don sáng

16

8086 ASM, 3 byte

Ví dụ này giả định rằng thời gian chạy C được liên kết trong.

jmp _malloc

địa điểm này lắp ráp đến e9 XX XXđâu XX XXlà địa chỉ tương đối của_malloc

Điều này gọi mallocđể phân bổ một lượng bộ nhớ không thể đoán trước và sau đó ngay lập tức trở lại, chấm dứt các quá trình. Trên một số hệ điều hành như DOS, bộ nhớ có thể không thể phục hồi được cho đến khi hệ thống được khởi động lại!


Việc triển khai malloc bình thường sẽ dẫn đến việc bộ nhớ được giải phóng khi thoát quá trình.
Joshua

@Joshua Vâng, nhưng đó là hành vi được xác định.
FUZxxl

12

Forth, 6 byte

Chơi gôn

s" " *

Phân bổ một chuỗi trống với s" ", để lại địa chỉ và độ dài (0) trên ngăn xếp, sau đó nhân chúng (dẫn đến một địa chỉ bộ nhớ bị mất).

Valgrind

%valgrind --leak-check=full gforth -e 's" " * bye'
...
==12788== HEAP SUMMARY:
==12788==     in use at exit: 223,855 bytes in 3,129 blocks
==12788==   total heap usage: 7,289 allocs, 4,160 frees, 552,500 bytes allocated
==12788== 
==12788== 1 bytes in 1 blocks are definitely lost in loss record 1 of 22
==12788==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12788==    by 0x406E39: gforth_engine (in /usr/bin/gforth-0.7.0)
==12788==    by 0x41156A: gforth_go (in /usr/bin/gforth-0.7.0)
==12788==    by 0x403F9A: main (in /usr/bin/gforth-0.7.0)
==12788== 
...
==12818== LEAK SUMMARY:
==12818==    definitely lost: 1 bytes in 1 blocks
==12818==    indirectly lost: 0 bytes in 0 blocks

10

đi 45 byte

package main
func main(){go func(){for{}}()}

điều này tạo ra một con goroutine ẩn danh với một vòng lặp vô hạn bên trong nó. chương trình có thể tiếp tục chạy như bình thường, vì bắt đầu goroutine giống như sinh ra một luồng nhỏ đang chạy đồng thời, nhưng chương trình không có cách nào để lấy lại bộ nhớ được phân bổ cho goroutine. người thu gom rác sẽ không bao giờ thu thập nó vì nó vẫn đang chạy. một số người gọi đây là 'rò rỉ một con khỉ đột'


kiểm tra golf: đây là 2 byte ngắn hơn so với cách gọi C.malloc(8), vì bạn cần phảiimport"C"
Đi xe đạp vào

9

Java 1.3, 23 byte

void l(){new Thread();}

Tạo một chủ đề nhưng không bắt đầu nó. Chuỗi được đăng ký trong nhóm luồng nội bộ, nhưng sẽ không bao giờ được bắt đầu, vì vậy không bao giờ kết thúc và do đó không bao giờ là ứng cử viên cho GC. Nó là một đối tượng không thể khắc phục được, bị mắc kẹt trong các chi Java.

Đó là một lỗi trong Java cho đến 1.3 bao gồm vì nó đã được sửa sau đó.

Kiểm tra

Chương trình sau đây đảm bảo gây ô nhiễm bộ nhớ với các đối tượng luồng mới và hiển thị dung lượng bộ nhớ trống giảm. Vì mục đích kiểm tra rò rỉ, tôi chuyên sâu làm cho GC chạy.

public class Pcg110485 {

    static
    void l(){new Thread();}

    public static void main(String[] args) {

        while(true){
            l();
            System.gc();
            System.out.println(Runtime.getRuntime().freeMemory());
        }
    }
}

Vì điều này chỉ hoạt động trên các phiên bản Java cụ thể, thay vào đó bạn nên nói "Java 3" trong tiêu đề của mình.

5
Không có thứ gì như Java 3. Đó là Java 1.3. Có Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Đánh số lạ, nhưng đó là như vậy.
Olivier Grégoire

Đây cũng là ý tưởng của tôi. Chỉ trích.
Bạch tuộc ma thuật Urn

8

Befunge ( nấm ), 1 byte

$

Điều này có thể phụ thuộc vào nền tảng và phụ thuộc phiên bản (tôi chỉ thử nghiệm với phiên bản 1.0.4 trên Windows), nhưng về mặt lịch sử , nấm là một trình thông dịch rất rò rỉ. Lệnh $(thả) không nên làm bất cứ điều gì trên một ngăn xếp trống, nhưng việc lặp lại mã này bằng cách nào đó quản lý để rò rỉ rất nhiều bộ nhớ rất nhanh. Trong vòng vài giây, nó sẽ sử dụng hết một vài hợp đồng biểu diễn và sẽ gặp sự cố với lỗi "hết bộ nhớ".

Lưu ý rằng nó không nhất thiết phải là một $lệnh - bất cứ điều gì sẽ làm. Nó sẽ không hoạt động với một tập tin nguồn trống. Phải có ít nhất một hoạt động.


8

Swift 3, 38 byte

Phiên bản mới:

class X{var x: X!};do{let x=X();x.x=x}

x có một tham chiếu mạnh mẽ đến chính nó, vì vậy nó sẽ không bị giải phóng, dẫn đến rò rỉ bộ nhớ.

Phiên bản cũ:

class X{var y:Y!}
class Y{var x:X!}
do{let x=X();let y=Y();x.y=y;y.x=x}

xchứa một tài liệu tham khảo mạnh mẽ yvà ngược lại. Do đó, cả hai sẽ không bị xử lý, dẫn đến rò rỉ bộ nhớ.


Hmm, bạn vẫn có thể tham chiếu bộ nhớ đó thông qua xy, vì vậy điều này không thực sự giống như một sự rò rỉ đối với tôi (trừ khi bạn phá hủy chúng bằng cách nào đó).
zeppelin

@zeppelin Dòng cuối cùng có thể được bọc trong một chức năng để khắc phục điều đó
NobodyNada

@NobodyNada, nếu tôi đặt dòng cuối cùng trong một dokhối sẽ khắc phục vấn đề zeppelin nêu ra phải không?
Daniel

@Dopapp Vâng; a dosẽ làm việc như là tốt. Ý tưởng tốt!
NobodyNada

Có thể rút ngắn lại, bạn không cần hai lớp - X có thể giữ tham chiếu cho chính nó:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński

7

Delphi (Object Pascal) - 33 byte

Tạo một đối tượng không có biến, chương trình bàn điều khiển đầy đủ:

program;begin TObject.Create;end.

Kích hoạt FastMM4 trong dự án sẽ hiển thị rò rỉ bộ nhớ:

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


6

C # - 84byte

class P{static void Main(){System.Runtime.InteropServices.Marshal.AllocHGlobal(1);}}

Điều này phân bổ chính xác 1 byte bộ nhớ không được quản lý, và sau đó mất đi IntPtr, mà tôi tin là cách duy nhất để có được hoặc giải phóng nó. Bạn có thể kiểm tra nó bằng cách nhồi nó vào một vòng lặp và chờ ứng dụng bị sập (có thể muốn thêm một vài số không để tăng tốc mọi thứ).

Tôi đã xem xét System.IO.File.Create("a");và như vậy, nhưng tôi không tin rằng đây là những rò rỉ bộ nhớ nhất thiết, vì chính ứng dụng sẽ thu thập bộ nhớ, đó là hệ điều hành bên dưới có thể bị rò rỉ (vì Closehoặc Disposekhông được gọi). Công cụ truy cập tệp cũng yêu cầu quyền hệ thống tệp và không ai muốn dựa vào những quyền đó. Và hóa ra điều này sẽ không bị rò rỉ, bởi vì không có gì ngăn cản người hoàn thiện được gọi (điều này giải phóng các tài nguyên cơ bản là có thể), mà khung bao gồm để giảm thiểu các loại phán đoán này (ở một mức độ nào đó), và để gây nhầm lẫn cho các lập trình viên với việc khóa tập tin dường như không xác định (nếu bạn là một người hoài nghi). Cảm ơn Jon Hanna đã đặt tôi thẳng vào vấn đề này.

Tôi hơi thất vọng vì tôi không thể tìm ra cách ngắn hơn. .NET GC hoạt động, tôi không thể nghĩ ra bất kỳ thứ gì IDisposablestrong mscorlib chắc chắn sẽ bị rò rỉ (và thực sự tất cả chúng dường như đều có chung kết, thật khó chịu) , tôi không biết cách nào khác để phân bổ bộ nhớ không được quản lý (thiếu PInvoke ) và phản ánh đảm bảo mọi thứ có tham chiếu đến nó (bất kể ngữ nghĩa ngôn ngữ (ví dụ: thành viên tư nhân hoặc các lớp không có người truy cập)) có thể được tìm thấy.


1
System.IO.File.Create("a")sẽ không rò rỉ bất cứ điều gì, nhưng GC.SuppressFinalize(System.IO.File.Create("a"))sẽ được yêu cầu rõ ràng là không chạy bộ hoàn thiện FileStreamsản xuất.
Jon Hanna

@JonHanna hoàn toàn đúng. Chứng hoang tưởng IDis của tôi dường như đã giúp tôi trở nên tốt hơn.
VisualMelon

Bạn có thể có cơ hội để rò rỉ GDI + bằng System.Drawing.Bitmap. Không biết nếu nó được tính bởi vì đó là một thư viện windows gây rò rỉ, chứ không phải chính chương trình.
BgrWorker

@BgrWorker họ không nghi ngờ gì nữa cũng có người quyết định và tôi có xu hướng tránh các thư viện bên ngoài trong môn đánh gôn vì tôi không đồng ý với sự đồng thuận về chi phí cho họ: nếu bạn có thể tìm thấy cách bạn tự tin, hãy đăng bài nó trong câu trả lời của riêng bạn!
VisualMelon

<!-- language: lang-c# -->Cảm ơn vì điều này & câu trả lời tốt đẹp! (Đó là C # vì vậy tôi yêu nó)
Metoniem

5

Yếu tố , 13 byte

Yếu tố có quản lý bộ nhớ tự động, nhưng cũng cho phép truy cập vào một số chức năng libc:

1 malloc drop

Phân bổ thủ công 1 byte bộ nhớ, trả về địa chỉ của nó và loại bỏ nó.

malloc thực sự đăng ký một bản sao để theo dõi rò rỉ bộ nhớ và giải phóng gấp đôi, nhưng xác định một bản sao bạn bị rò rỉ không phải là một nhiệm vụ dễ dàng.

Nếu bạn muốn chắc chắn rằng bạn thực sự mất tham chiếu đó:

1 (malloc) drop

Kiểm tra rò rỉ với [ 1 malloc drop ] leaks.nói:

| Disposable class | Instances |                    |
| malloc-ptr       | 1         | [ List instances ] |

Kiểm tra rò rỉ với [ 1 (malloc) drop ] leaks.nói:

| Disposable class | Instances | |

Ôi không! Yếu tố tồi tệ, giờ đã mắc bệnh Alzheimer! CƯỜI MỞ MIỆNG:


5

Lisp thường gặp (chỉ SBCL), 28 26 byte

sb-alien::(make-alien int)

Bạn chạy nó như vậy : sbcl --eval 'sb-alien::(make-alien int)'; không có gì được in cũng không được trả lại, nhưng việc cấp phát bộ nhớ xảy ra. Nếu tôi bọc biểu mẫu bên trong a (print ...), con trỏ được hiển thị trong REPL.

  1. package::(form)là một ký hiệu đặc biệt trong SBCL để tạm thời ràng buộc gói hiện tại trong khi đọc một biểu mẫu. Điều này được sử dụng ở đây để tránh tiền tố cả make-alienintvới sb-alien. Tôi nghĩ sẽ là gian lận khi cho rằng gói hiện tại được đặt thành gói này, vì đó không phải là trường hợp khi khởi động.

  2. make-alien cấp phát bộ nhớ cho một loại nhất định và kích thước tùy chọn (sử dụng malloc).

  3. Khi thực hiện điều này trong REPL, hãy thêm 0sau khi cấp phát để REPL không trả về con trỏ, nhưng thay vào đó là giá trị đó. Nếu không, điều đó không có sẽ là một sự rò rỉ thực vì REPL nhớ ba giá trị trở lại cuối cùng (xem *, **,*** ) và chúng tôi vẫn có thể có một cơ hội để giải phóng bộ nhớ được phân bổ.

2 byte bị xóa nhờ PrzemysławP, cảm ơn!


1
Bạn không thể sử dụng 1(hoặc 2, 3vv) thay vì ()để bạn quay trở lại giá trị 1? Nó sẽ tiết kiệm 1 byte. Ngoài ra câu trả lời này chỉ REPL? Có lẽ nếu bạn tải mã với loadbạn không thể bao gồm ()hoặc bất cứ điều gì ở cuối, bởi vì dù sao nó sẽ không thể truy cập được?
PrzemysławP

1
@ PrzemysławP Bạn nói đúng cả hai điểm, tôi đã thử evalvà điều này hoạt động như bạn đã nói. Cảm ơn rất nhiều!
coredump

4

Tự động , 39 byte

#include<Memory.au3>
_MemGlobalAlloc(1)

Phân bổ một byte từ heap. Vì tay cầm được trả về _MemGlobalAllocbị loại bỏ, không có cách nào để giải phóng rõ ràng sự phân bổ đó.


3

C ++, 16 byte

main(){new int;}

Tôi không có valgrind để kiểm tra rò rỉ, nhưng khá chắc chắn rằng nó nên.Nếu không tôi sẽ thử:

main(){[]{new int;}();}

Kết quả có giá trị

(Nó thực sự bị rò rỉ)

==708== LEAK SUMMARY:
==708==    definitely lost: 4 bytes in 1 blocks

@WheatWizard Tôi đang sử dụng g++ 4.3.2(không phải là gần đây nhất) và nó biên dịch tốt. Không có loại trả lại inttheo mặc định tôi nghĩ. Với -Walltôi có một cảnh báo mặc dù:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@WheatWizard Xin lỗi, tôi chỉ thấy bạn đã đưa ra ví dụ c ++ để bắt đầu cuộc thi. Đến từ reddit tôi chỉ nhìn vào các câu trả lời và (lạ thay) không thấy bất kỳ C ++ nào. Tôi cảm thấy hơi ngớ ngẩn. : /
matovitch

Đồng thuận là bạn có thể tính giống []{new int;}như một hàm C ++ (thử thách không chỉ định toàn bộ chương trình).
Toby Speight

3

Java (OpenJDK 9) , 322 220 byte

import sun.misc.*;class Main{static void main(String[]a)throws Exception{java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible‌​(1<2);((Unsafe)f.get‌​(1)).allocateMemory(‌​1);}}

Hãy thử trực tuyến!

Đây là một rò rỉ bộ nhớ khác không sử dụng String Cache. Nó phân bổ một nửa RAM của bạn và bạn không thể làm gì với nó.

Cảm ơn zeppelin đã lưu tất cả các byte


Bạn có thể lưu một loạt byte bằng cách lấy Unsafecá thể từ biến tĩnh bên trong nó, như thế:import sun.misc.*;class M{static void main(String[]a)throws Exception{java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(1<2);((Unsafe)f.get(1)).allocateMemory(1);}}
zeppelin

Và bạn có thể tiết kiệm thêm một số bằng cách thay thế public static void mainbằng trình khởi tạo tĩnh static{try{}catch(Exception e){}}(có thể khó hơn một chút để khởi chạy nhưng vẫn hợp lệ và có thể biên dịch được).
zeppelin

y sử dụng hàm tạo đã được sử dụng trong phiên bản mã Android tôi đã sử dụng. Tôi sẽ thay đổi một số thứ khi im @home nhưng tôi sẽ đi theo con đường bạn đã đi với một tuyên bố duy nhất;)
Serverfrog

Xóa khoảng trắng, sử dụng athay vì argsvà xóa công khai. tio.run/nexus/ Kẻ
Pavel

đúng có thể được thay thế bằng 1> 0
masterX244

3

c, 9 byte

main(){}

Bằng chứng:

localhost/home/elronnd-10061: cat t.c
main(){}
localhost/home/elronnd-10062: valgrind gcc t.c
==10092== Memcheck, a memory error detector
==10092== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10092== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==10092== Command: gcc t.c
==10092==
t.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main(){}
 ^~~~
==10092==
==10092== HEAP SUMMARY:
==10092==     in use at exit: 178,518 bytes in 73 blocks
==10092==   total heap usage: 362 allocs, 289 frees, 230,415 bytes allocated
==10092==
==10092== LEAK SUMMARY:
==10092==    definitely lost: 4,659 bytes in 8 blocks
==10092==    indirectly lost: 82 bytes in 5 blocks
==10092==      possibly lost: 0 bytes in 0 blocks
==10092==    still reachable: 173,777 bytes in 60 blocks
==10092==         suppressed: 0 bytes in 0 blocks
==10092== Rerun with --leak-check=full to see details of leaked memory
==10092==
==10092== For counts of detected and suppressed errors, rerun with: -v
==10092== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

1
Bạn không thực sự bị rò rỉ bộ nhớ; gccLà. Điều này cũng sẽ làm việc với chương trình trống. Hãy thử gcc src.c && valgrind ./a.out, mà sẽ tạo ra một kết quả sạch.

3

C #, 109 byte

public class P{static void Main({for(;;)System.Xml.Serialization.XmlSerializer.FromTypes(new[]{typeof(P)});}}

Chúng tôi tìm thấy ý tưởng đằng sau sự rò rỉ này trong mã sản xuất và nghiên cứu nó dẫn đến bài viết này. Vấn đề chính là trong trích dẫn dài từ bài viết này (đọc nó để biết thêm thông tin):

Tìm kiếm mã của tôi cho PurchaseOrder, tôi tìm thấy dòng mã này trong page_loadmột trong các trang của tôiXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Điều này có vẻ như là một đoạn mã khá ngây thơ. Chúng tôi tạo ra XMLSerializercho PurchaseOrder. Nhưng những gì xảy ra dưới vỏ bọc?

Nếu chúng ta nhìn vào hàm XmlSerializertạo với Reflector, chúng ta thấy rằng nó gọi nó this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);sẽ tạo ra một tổ hợp temp (động). Vì vậy, mỗi khi mã này chạy (tức là mỗi khi trang được nhấn), nó sẽ tạo ra một hội đồng mới.

Lý do nó tạo ra một hội đồng là nó cần tạo ra các hàm để tuần tự hóa và giải tuần tự hóa và những thứ này cần phải nằm ở đâu đó.

Ok, tốt, nó tạo ra một hội đồng, vậy thì sao? Khi chúng ta hoàn thành nó, nó sẽ biến mất phải không?

Vâng, một hội đồng không phải là một đối tượng trên Heap của GC, GC thực sự không biết về các hội đồng, vì vậy nó sẽ không được thu gom rác. Cách duy nhất để thoát khỏi các hội đồng trong 1.0 và 1.1 là hủy tải miền ứng dụng mà nó cư trú.

Và đó là vấn đề Tiến sĩ Watson.

Chạy từ trình biên dịch trong Visual Studio 2015 và sử dụng Cửa sổ Công cụ Chẩn đoán sẽ hiển thị các kết quả sau sau khoảng 38 giây. Lưu ý Bộ nhớ quy trình đang tăng đều đặn và Trình thu gom rác (GC) tiếp tục chạy nhưng không thể thu thập bất cứ thứ gì.

Cửa sổ công cụ chẩn đoán


2

C 30 byte

f(){int *i=malloc(sizeof(4));}

Kết quả Valgrind:

         ==26311== HEAP SUMMARY:
         ==26311==     in use at exit: 4 bytes in 1 blocks
         ==26311==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated
         ==26311== 
         ==26311== LEAK SUMMARY:
         ==26311==    definitely lost: 4 bytes in 1 blocks
         ==26311==    indirectly lost: 0 bytes in 0 blocks
         ==26311==      possibly lost: 0 bytes in 0 blocks
         ==26311==    still reachable: 0 bytes in 0 blocks
         ==26311==         suppressed: 0 bytes in 0 blocks
         ==26311== Rerun with --leak-check=full to see details of leaked memory

2
Có thể thay vì chỉ làm main(){malloc(1);}?
kirbyfan64sos

@Yea nó là! Nhưng nó đã được đăng!
Abel Tom

2

Phi tiêu, 76 byte

import'dart:async';main()=>new Stream.periodic(Duration.ZERO).listen((_){});

Một chút giống như câu trả lời JavaScript. Khi bạn gọi.listen một đối tượng luồng Dart, bạn sẽ được trả lại một StreamSubcription, cho phép bạn ngắt kết nối khỏi luồng. Tuy nhiên, nếu bạn vứt nó đi, bạn không bao giờ có thể hủy đăng ký khỏi luồng, gây rò rỉ. Cách duy nhất có thể khắc phục rò rỉ là nếu chính Stream được thu thập, nhưng nó vẫn được tham chiếu bên trong bởi một kết hợp StreamContoder + Timer.

Thật không may, Dart quá thông minh cho những thứ khác mà tôi đã thử. ()async=>await new Completer().futurekhông hoạt động vì sử dụng await cũng giống như làmnew Completer().future.then(<continuation>) , điều này cho phép chính việc đóng cửa bị phá hủy, Trình hoàn thành thứ hai không được tham chiếu (Trình hoàn thành giữ tham chiếu đến Tương lai từ.future , Tương lai giữ tham chiếu đến phần tiếp theo là đóng).

Ngoài ra, Isolates (còn gọi là các luồng) được dọn sạch bởi GC, do đó sinh ra trong một luồng mới và ngay lập tức tạm dừng nó (import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true); ) không hoạt động. Thậm chí sinh ra một Isolate với một vòng lặp vô hạn ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) sẽ giết chết Isolate và thoát khỏi chương trình.

Ồ tốt


Nếu chương trình chính của bạn tiếp tục chạy và làm những thứ khác, liệu trình thu gom rác có thể dừng cách ly không? Tôi hỏi bởi vì ví dụ về con goroutine của tôi nghe có vẻ tương tự ... tôi cho rằng thực tế là chương trình thoát ra và trả lại tất cả bộ nhớ cho HĐH không có nghĩa là nó không bị rò rỉ.
don sáng

2

Swift, 12 byte

[3,5][0...0]

Giải trình:

Đây là rò rỉ bộ nhớ thực tế có thể xảy ra ở bất kỳ ngôn ngữ nào, bất kể ngôn ngữ đó có sử dụng quản lý thủ công bộ nhớ, đếm tham chiếu tự động (ARC, như Swift) hay thậm chí quét bộ sưu tập rác.

[3,5]chỉ là một mảng theo nghĩa đen. Mảng này phân bổ đủ bộ nhớ cho ít nhất 2 yếu tố này. Các 35chỉ là tùy ý.

Đăng ký (lập chỉ mục) một Array<T>sản xuất một ArraySlice<T>. AnArraySlice<T> là một khung nhìn vào bộ nhớ của Array mà nó được tạo từ đó.

[3,5][0...0]tạo ra một ArraySlice<Int>, có giá trị là [3]. Lưu ý rằng 3trong lát này là 3phần tử giống như 3trong bản gốc Arrayđược hiển thị ở trên, không phải là bản sao.

Các lát kết quả sau đó có thể được lưu trữ vào một biến và được sử dụng. Mảng ban đầu không còn được tham chiếu, vì vậy bạn sẽ nghĩ rằng nó có thể được giải quyết. Tuy nhiên, nó không thể.

Vì lát cắt hiển thị chế độ xem vào bộ nhớ của mảng mà nó xuất phát, nên mảng ban đầu phải được giữ nguyên miễn là lát cắt tồn tại. Vì vậy, của bản gốc2 giá trị bộ nhớ kích thước phần tử được phân bổ, chỉ có giá trị bộ nhớ kích thước phần tử đầu tiên được sử dụng, với bộ nhớ còn lại được yêu cầu tồn tại để không phân bổ đầu tiên. Kích thước phần tử thứ hai của bộ nhớ là yếu tố bị rò rỉ.

Giải pháp cho vấn đề này là không giữ các lát nhỏ của mảng lớn tồn tại lâu. Nếu bạn cần duy trì nội dung lát, sau đó quảng cáo nó thành Mảng, điều này sẽ kích hoạt bộ nhớ được sao chép, do đó loại bỏ sự phụ thuộc vào bộ nhớ của mảng ban đầu:

Array([3,5][0...0])

2

Giải pháp 1: C (Mac OS X x86_64), 109 byte

Nguồn cho golf_sol1.c

main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};

Chương trình trên cần được biên dịch với quyền truy cập thực thi trên phân đoạn __DATA.

clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx

Sau đó, để thực hiện chương trình chạy như sau:

./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')

Các kết quả:

Thật không may, Valgrind không xem bộ nhớ được phân bổ từ các cuộc gọi hệ thống, vì vậy tôi không thể hiển thị một rò rỉ được phát hiện đẹp.

Tuy nhiên, chúng ta có thể nhìn vào vmmap để thấy phần lớn bộ nhớ được phân bổ (siêu dữ liệu MALLOC).

                                VIRTUAL   REGION 
REGION TYPE                        SIZE    COUNT (non-coalesced) 
===========                     =======  ======= 
Kernel Alloc Once                    4K        2 
MALLOC guard page                   16K        4 
MALLOC metadata                   16.2M        7 
MALLOC_SMALL                      8192K        2         see MALLOC ZONE table below
MALLOC_TINY                       1024K        2         see MALLOC ZONE table below
STACK GUARD                       56.0M        2 
Stack                             8192K        3 
VM_ALLOCATE (reserved)             520K        3         reserved VM address space (unallocated)
__DATA                             684K       42 
__LINKEDIT                        70.8M        4 
__TEXT                            5960K       44 
shared memory                        8K        3 
===========                     =======  ======= 
TOTAL                            167.0M      106 
TOTAL, minus reserved VM space   166.5M      106 

Giải trình

Vì vậy, tôi nghĩ rằng tôi cần mô tả những gì thực sự đang diễn ra ở đây, trước khi chuyển sang giải pháp cải tiến.

Hàm chính này đang lạm dụng khai báo kiểu thiếu của C (vì vậy nó mặc định là int mà không cần chúng ta lãng phí các ký tự viết nó), cũng như cách các ký hiệu hoạt động. Trình liên kết chỉ quan tâm đến việc nó có thể tìm thấy một biểu tượng được gọi làmain để gọi hay không. Vì vậy, ở đây chúng tôi đang tạo ra một mảng int mà chúng tôi đang khởi tạo với shellcode của chúng tôi sẽ được thực thi. Vì lý do này, main sẽ không được thêm vào phân đoạn __TEXT mà là phân đoạn __DATA, lý do chúng tôi cần biên dịch chương trình với phân đoạn __DATA có thể thực thi được.

Shellcode được tìm thấy trong main là như sau:

movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret

Điều này đang làm là gọi hàm syscall để phân bổ một trang của bộ nhớ (machramm_allocate sử dụng nội bộ). RAX phải bằng 0x100000a (cho biết tòa nhà mà chúng ta muốn), trong khi RDI giữ mục tiêu phân bổ (trong trường hợp của chúng tôi, chúng tôi muốn đây là mach_task_elf ()), RSI nên giữ địa chỉ để ghi con trỏ vào bộ nhớ mới được tạo (vì vậy chúng tôi chỉ trỏ nó vào một phần trên ngăn xếp), RDX giữ kích thước của phân bổ (chúng tôi chỉ chuyển qua RAX hoặc 0x100000a chỉ để lưu byte), R10 giữ các cờ (chúng tôi cho biết nó có thể được phân bổ ở bất cứ đâu).

Bây giờ không rõ ràng rõ ràng nơi RAX và RDI đang nhận được giá trị của họ từ đó. Chúng tôi biết RAX cần phải là 0x100000a và RDI cần phải là giá trị mach_task_elf () trả về. May mắn thay mach_task_elf () thực sự là một macro cho một biến (mach_task_elf_), ở cùng một địa chỉ bộ nhớ mỗi lần (tuy nhiên nên thay đổi khi khởi động lại). Trong trường hợp cụ thể của tôi, mach_task_elf_ tình cờ được đặt tại 0x00007fff7d578244. Vì vậy, để cắt giảm các hướng dẫn, thay vào đó chúng ta sẽ chuyển dữ liệu này từ argv. Đây là lý do tại sao chúng tôi chạy chương trình với biểu thức này$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')cho đối số đầu tiên. Chuỗi là hai giá trị được kết hợp, trong đó giá trị RAX (0x100000a) chỉ có 32 bit và có phần bổ sung của một người được áp dụng cho nó (vì vậy không có byte rỗng; chúng tôi chỉ KHÔNG giá trị để lấy bản gốc), giá trị tiếp theo là RDI (0x00007fff7d578244) đã được dịch chuyển sang bên trái với 2 byte rác bổ sung được thêm vào cuối (một lần nữa để loại trừ các byte null, chúng tôi chỉ cần chuyển nó về bên phải để đưa nó trở lại ban đầu).

Sau tòa nhà, chúng tôi viết thư cho bộ nhớ mới được phân bổ của chúng tôi. Lý do cho điều này là do bộ nhớ được phân bổ bằng mach_vm_allocate (hoặc tòa nhà này) thực sự là các trang VM và không được tự động phân trang vào bộ nhớ. Thay vào đó chúng được bảo lưu cho đến khi dữ liệu được ghi vào chúng, và sau đó những trang đó được ánh xạ vào bộ nhớ. Không chắc chắn nếu nó sẽ đáp ứng các yêu cầu nếu nó chỉ được bảo lưu.

Đối với giải pháp tiếp theo, chúng tôi sẽ tận dụng thực tế là shellcode của chúng tôi không có byte rỗng và vì vậy có thể di chuyển nó ra ngoài mã chương trình của chúng tôi để giảm kích thước.

Giải pháp 2: C (Mac OS X x86_64), 44 byte

Nguồn cho golf_sol2.c

main[]={141986632,10937,1032669184,2,42227};

Chương trình trên cần được biên dịch với quyền truy cập thực thi trên phân đoạn __DATA.

clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx

Sau đó, để thực hiện chương trình chạy như sau:

./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')

Kết quả sẽ giống như trước đây, vì chúng tôi đang thực hiện phân bổ có cùng kích thước.

Giải trình

Theo nhiều khái niệm tương tự như giải pháp 1, ngoại trừ việc chúng tôi đã chuyển đoạn mã bị rò rỉ ra bên ngoài chương trình.

Mã shell được tìm thấy trong chính bây giờ là như sau:

movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)

Điều này về cơ bản sao chép shellcode mà chúng ta chuyển trong argv để theo sau mã này (vì vậy sau khi đã sao chép nó, nó sẽ chạy shellcode được chèn). Điều có lợi cho chúng tôi là phân đoạn __DATA sẽ có kích thước tối thiểu là một trang, vì vậy ngay cả khi mã của chúng tôi không lớn, chúng tôi vẫn có thể "viết" một cách an toàn hơn. Nhược điểm là giải pháp lý tưởng ở đây, thậm chí sẽ không cần bản sao, thay vào đó, nó sẽ chỉ gọi và thực thi shellcode trong argv trực tiếp. Nhưng thật không may, bộ nhớ này không có quyền thực thi. Chúng tôi có thể thay đổi quyền của bộ nhớ này, tuy nhiên, nó sẽ yêu cầu nhiều mã hơn là chỉ sao chép nó. Một chiến lược thay thế sẽ là thay đổi các quyền từ một chương trình bên ngoài (nhưng nhiều hơn về điều đó sau).

Mã shell chúng tôi chuyển đến argv như sau:

movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret

Mã này giống với mã trước đây của chúng tôi, chỉ khác là chúng tôi bao gồm các giá trị cho EAX và RDI trực tiếp.

Giải pháp có thể 1: C (Mac OS X x86_64), 11 byte

Ý tưởng sửa đổi chương trình bên ngoài, cho chúng ta giải pháp khả thi để chuyển leaker sang một chương trình bên ngoài. Trong đó chương trình thực tế của chúng tôi (trình) chỉ là một chương trình giả, và chương trình leaker sẽ phân bổ một số bộ nhớ trong chương trình mục tiêu của chúng tôi. Bây giờ tôi không chắc chắn nếu điều này sẽ nằm trong quy tắc cho thử thách này, nhưng dù sao cũng chia sẻ nó.

Vì vậy, nếu chúng tôi sử dụng mach_vm_allocate trong một chương trình bên ngoài với mục tiêu được đặt cho chương trình thử thách của chúng tôi, điều đó có nghĩa là chương trình thử thách của chúng tôi chỉ cần là một thứ gì đó theo:

main=65259;

Trong đó shellcode chỉ đơn giản là một bước nhảy ngắn đến chính nó (bước nhảy / vòng lặp vô hạn), vì vậy chương trình vẫn mở và chúng ta có thể tham chiếu nó từ một chương trình bên ngoài.

Giải pháp có thể 2: C (Mac OS X x86_64), 8 byte

Thật thú vị khi tôi nhìn vào sản lượng valgrind, tôi thấy rằng ít nhất là theo valgrind, bộ nhớ bị rò rỉ. Vì vậy, hiệu quả mỗi chương trình đang rò rỉ một số bộ nhớ. Với trường hợp này, chúng ta thực sự có thể tạo ra một chương trình không có gì (chỉ đơn giản là thoát) và đó thực sự sẽ bị rò rỉ bộ nhớ.

Nguồn:

main(){}


==55263== LEAK SUMMARY:
==55263==    definitely lost: 696 bytes in 17 blocks
==55263==    indirectly lost: 17,722 bytes in 128 blocks
==55263==      possibly lost: 0 bytes in 0 blocks
==55263==    still reachable: 0 bytes in 0 blocks
==55263==         suppressed: 16,316 bytes in 272 blocks

2

Tiếng Anh đơn giản , 71 70 58 35 byte

Đã xóa 1 byte bằng cách xóa một dòng trống. Đã xóa 12 byte bằng cách loại bỏ định nghĩa loại "bogon" và sử dụng loại "điều" cha thay vì kiểu con "bogon". Đã xóa 23 byte bằng cách chuyển từ một chương trình hoàn chỉnh, thành một thói quen làm rò rỉ bộ nhớ.

Phiên bản chơi gôn:

To x:
Allocate memory for a thing.

Phiên bản Ungolfed là một chương trình hoàn chỉnh, sử dụng định nghĩa kiểu con và không rò rỉ bộ nhớ:

A bogon is a thing.

To do something:
  Allocate memory for a bogon.
  Destroy the bogon.

To run:
  Start up.
  Do something.
  Shut down.

Nếu phiên bản được đánh gôn của "x" được gọi, nó sẽ rò rỉ bộ nhớ theo tỷ lệ với số lần "x" được gọi. Trong phiên bản chơi gôn, "Giải quyết vấn đề." sẽ sửa lỗi rò rỉ bộ nhớ.

Theo mặc định, tiếng Anh kiểm tra rò rỉ bộ nhớ. Khi phiên bản rò rỉ bộ nhớ được chạy, một hộp thoại sẽ xuất hiện ngay trước khi chương trình tắt. Hộp thoại có tiêu đề "gỡ lỗi", thông báo "1 nhỏ giọt" và nút "OK". Chức năng rò rỉ được gọi càng nhiều lần, số lượng "nhỏ giọt" trong tin nhắn càng lớn. Khi phiên bản không rò rỉ bộ nhớ được chạy, hộp thoại sẽ không xuất hiện.

Trong tiếng Anh đơn giản, một "thứ" là một con trỏ tới một mục trong danh sách liên kết đôi. "Điều", "để khởi động" và "tắt" được định nghĩa trong một mô-đun gọi là "mì", cần được sao chép (thường là một tệp riêng biệt) vào mỗi dự án. "A", "the", "to", "để phân bổ bộ nhớ" và "để hủy" được định nghĩa trong trình biên dịch.

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.