88
\$\begingroup\$

Your task is to write code that will leak at least one byte of memory in as few bytes as possible. The memory must be leaked not just allocated.

Leaked memory is memory that the program allocates but loses the ability to access before it can deallocate the memory properly. For most high level languages this memory has to be allocated on the heap.

An example in C++ would be the following program:

int main(){new int;}

This makes a new int on the heap without a pointer to it. This memory is instantly leaked because we have no way of accessing it.

Here is what a leak summary from Valgrind might look like:

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

Many languages have a memory debugger (such as Valgrind) if you can you should include output from such a debugger to confirm that you have leaked memory.

The goal is to minimize the number of bytes in your source.

\$\endgroup\$
11
  • 2
    \$\begingroup\$ Perhaps you could have different ranges of amount leaked and depending on how much you leak you lose x% of your byte count \$\endgroup\$
    – user63187
    Commented Feb 18, 2017 at 17:38
  • 11
    \$\begingroup\$ @ChristopherPeart For one I am not a fan of bonuses on challenges and for two as you have already shown it is very easy to leak unbounded memory. \$\endgroup\$
    – Wheat Wizard
    Commented Feb 18, 2017 at 18:01
  • 1
    \$\begingroup\$ Related. Not a duplicate, though, because most answers to that question form an infinite reachable structure in memory rather than actually leaking memory. \$\endgroup\$
    – user62131
    Commented Feb 18, 2017 at 18:18
  • 2
    \$\begingroup\$ what is the idea? That the mem cannot be freed? I guess this would require native execution for garbage collected languages or exploiting bugs. \$\endgroup\$ Commented Feb 18, 2017 at 21:26
  • 7
    \$\begingroup\$ I see how languages designed for golfing fail miserably on this one ... \$\endgroup\$
    – Kh40tiK
    Commented Feb 19, 2017 at 12:25

42 Answers 42

1
2
2
\$\begingroup\$

Haskell, 29 bytes

Yet another "malloc and forget" answer:

import Foreign
main=()<$new 0

This time it is in Haskell, which is normally a garbage collected language, but the Foreign Function Interface gives access to raw malloc operations.

Explanation

The function new allocates a new block of memory, stores a copy of the argument at that location and returns a pointer to it. In this case, we store the value 0 :: Double. Then, we ignore the pointer by using (<$) :: a -> IO (Ptr Double) -> IO a to replace the return value with the unit (()).

\$\endgroup\$
2
\$\begingroup\$

Swift, 42 38 bytes

class a{var b: a};var b:a?=a();b!.b=b;b=nil

Explanation

Swift uses automatic reference counting (ARC) for garbage collection. It works by keeping a count of the number of references to an object, and deallocates it right before the reference count diminishes to zero.
Take a look at the following pseudocode:

let x be an address of allocated memory
allocate 16 bytes, then store the address of those 16 bytes in x

One of the worst things to do is...

set x to NULL

Because now we no longer know where those 16 bytes are located.

ARC prevents (most) objects from being made inaccessible. However, what ARC doesn't help with is cycles, or reference loops.
For example, \$a\$ has a reference to \$b\$, and \$b\$ has a reference to \$a\$. The problem is that they rely on each other to keep a non-zero reference count. Recall that the above code looks like

class a{var b: a!};do{var b:a?=a();b!.b=b;b=nil}

The first part...

class a {
   var b: a!
}

...lays the foundation for a reference cycle.

var b: a? = a();

Sets b to to a new a, and b allowed to be nil. Right now, the reference count is 1.

b!.b = b

Assert that b is not nil (because it's allowed to be, as it will be later), set property b of b to b. This creates a cycle; b has a reference to b. Right now, the reference count is 2, but only one of those references come from an accessible source, namely b.

b = nil

Whoops. The reference count is 1, because b has a reference to b, but now, nothing else has a reference to b, creating a memory leak.

\$\endgroup\$
1
\$\begingroup\$

x86 ASM, 1 byte, assumes a certain kind of stack frame

push AX

Some kinds of stack frames free this at mov SP, BP, some don't. The kinds that don't probably crash but you can avoid that by not returning. For some reason this code golf challenge does not require placing the leak inside a loop as it ought to.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The language here is machine code, not assembler (it's 7 bytes in assembler), so you might want to give a hexdump of the machine code. However, I don't see how this leaks memory; by definition, this memory is deallocated when the function returns, and corrupting the stack in such a way that the function crashes on return doesn't change that. \$\endgroup\$
    – user62131
    Commented Feb 20, 2017 at 23:38
  • 2
    \$\begingroup\$ @ais523: They changed the counting rules for assembly so its byte count is as the machine code. \$\endgroup\$
    – Joshua
    Commented Feb 21, 2017 at 4:12
1
\$\begingroup\$

PHP, 32 bytes

gc_disable();$x[]=&$x;unset($x);

Run like this:

php -r 'gc_disable();$x[]=&$x;unset($x);'

Explanation

This causes a circular reference, which can only be caught (and freed) by the circular reference collector (which I disabled). Test to see memory leak effects:

# Without disabling circular ref collector.
php -nr 'for(;$a++<100000;){$x[]=&$x;unset($x);}echo memory_get_usage()."\n";'
> 353944
# Circular ref collector disabled.
php -nr 'gc_disable();for(;$a++<100000;){$x[]=&$x;unset($x);}echo memory_get_usage()."\n";'
> 40354096
\$\endgroup\$
1
\$\begingroup\$

ZX Spectrum BASIC, 2 bytes

GO SUB PI

Explanation: both GO SUB and PI are single tokens taking one byte. GO SUB will actually jump to line nr. 3, but the number 3 would take 5 bytes whereas PI takes only one.

ZX Spectrum BASIC has no memory management, so the return address on the stack remains until you re-initialize BASIC with NEW or CLEAR or RUN a program or until you use the RETURN command. If used as a part of a stored BASIC program, the memory would remain leaked even after the program terminates (and if you repeat the GO SUB PI enough times, the machine will run out of memory).

\$\endgroup\$
1
\$\begingroup\$

Julia, 60 56 bytes

addprocs();!a=while 1>0 s=convert(SharedArray,a)end;![9]

This entry is based on the bug https://github.com/JuliaLang/julia/issues/20424 , and is very likely to be invalidated by a future update.

It can be done with less code, but the loop nails it down that it's an actual leak, caused by a bug, and not just the GC waiting its merry time before it starts collecting garbage. Also, GC will probably be alerted and start cleaning everything up if you start putting any significant amount of other garbage onto the heap, so I want to get the OOM error up before anyone starts cluttering the heap with other garbage.

Explained (as far as I can tell):

#Spawn some child processes
addprocs();

#Iterative subroutine that does nothing but generate and then immediately forget shared arrays en masse
!a=while 1>0 s=convert(SharedArray,a)end;

#Let it rip
#For whatever reason, GC never gets around to cleaning the shared arrays up
#The OutOfMemoryError should pop up soon enough
![9]
\$\endgroup\$
1
\$\begingroup\$

Batch, 2 bytes

a^

This is based on my favorite StackOverflow post of all time. (Wayback link since the original is removed.) I'll copy the explanation directly from there:

The Bug Explained

The bug is that when a caret is detected, the next character is read from the file (to be 'escaped'). If the caret is the last character of the file, this results in a logic error, as when a call to get_next_char is made, the file pointer is incremented by one; in this case that puts it passed the EOF. Since the EOF is effectively ignored when the command parser continues to read the next input, it essentially 'resets' its file pointer due to the EOF+1 error. In this case, putting the file pointer to EOF+1 causes the pointer to be at some large negative number, and since file's can't go below 0, it's file pointer is basically reset to 0 and the parsing continues from the beginning of the file.

It should be noted that, based on my own testing, the issue seems to be resolved in Windows 10.

\$\endgroup\$
1
\$\begingroup\$

Bash (4.4.20), 0 bytes

Verification

$ valgrind bash main.sh
==1172== Memcheck, a memory error detector
==1172== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1172== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==1172== Command: bash main.sh
==1172== 
==1172== 
==1172== HEAP SUMMARY:
==1172==     in use at exit: 33,849 bytes in 379 blocks
==1172==   total heap usage: 514 allocs, 135 frees, 54,210 bytes allocated
==1172== 
==1172== LEAK SUMMARY:
==1172==    definitely lost: 12 bytes in 1 blocks
==1172==    indirectly lost: 0 bytes in 0 blocks
==1172==      possibly lost: 0 bytes in 0 blocks
==1172==    still reachable: 33,837 bytes in 378 blocks
==1172==         suppressed: 0 bytes in 0 blocks
==1172== Rerun with --leak-check=full to see details of leaked memory
==1172== 
==1172== For counts of detected and suppressed errors, rerun with: -v
==1172== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
\$\endgroup\$
0
\$\begingroup\$

SmileBASIC, 8 bytes

?POP(@L)

Due to a bug, using POP on a string literal will use up memory, which can't be freed without restarting the system.

\$\endgroup\$
0
\$\begingroup\$

Swift 5.10 (with C interop), 23 bytes

Well of course the C-interop solution would be the shortest, I don't know what I expected.

import Darwin
malloc(1)

If you're on a Linux box, you'll need to replace Darwin with Glibc. (Incidentally, this saves a byte.)

Swift 5.10 (pure), 42 bytes

({UnsafeMutablePointer<()>.allocate})()(1)

This is basically a direct Swift port of the malloc(_:) call above.

\$\endgroup\$
-1
\$\begingroup\$

Javascript, 3 bytes

Every string in Javascript is re-created in memory, since they are immutable.

If you allocate a string with something, it will take (at least) 2 bytes (UTF-16).

"1"

Running something like for(var i = 0; i < 1000000; i++)"1";, on Google Chrome, you can see the memory increasing (using the built-in task manager).
It climbs by 10-20kb, using the example code.

\$\endgroup\$
11
  • 6
    \$\begingroup\$ AFAIK garbage collector will reclaim these orphaned strings, freeing the memory. \$\endgroup\$
    – zeppelin
    Commented Feb 19, 2017 at 6:02
  • \$\begingroup\$ @zeppelin I haven't seen that effect on Google Chrome. \$\endgroup\$ Commented Feb 19, 2017 at 6:17
  • 3
    \$\begingroup\$ @IsmaelMiguel I highly suspect that while the memory is not freed immediately Javascript Garbage collector will deallocate them at the end of the process. Your loop does not demonstrate that anything has been leaked only that things are being allocated. \$\endgroup\$
    – Wheat Wizard
    Commented Feb 19, 2017 at 6:20
  • 1
    \$\begingroup\$ @IsmaelMiguel The question is whether or not the memory is de-allocated, by Javascript's garbage collector. I am not an expert in Javascript, so I really don't know how the garbage collector works but your answer does not really convince me that the memory is leaked. Can you run it through any sort of memory debugger? \$\endgroup\$
    – Wheat Wizard
    Commented Feb 19, 2017 at 6:34
  • 1
    \$\begingroup\$ @IsmaelMiguel in the case of JavaScript, allocations unreachable by the GC, for a (non-golfing) example: document.body.addEventListener("click", ()=>{}) since the event listener handle is not retained, it can't be removed programmatically, and since its attached to a DOM node the GC can't remove it. Its the same thing as the setTimeout answer. \$\endgroup\$ Commented Feb 21, 2017 at 18:54
-2
\$\begingroup\$

C/C++, GCC; 46 bytes

int main(){while(1){int* x=new int;}return 0;}

What does it do?

int main()
{
    // Start an infinite loop:
    while (1)
    {
        // Create a variable `x` and point to a new integer:
      int* x = new int;
    }

    return 0;
}

The code above uses an infinite loop to point to a new address that is not deleted.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ No, No, No! new isn't a C operator, and your code could be made much shorter anyways. For C++, a shorter code would be int main(){new int;}, for C, a shorter code would be void*malloc(size_t);main(){malloc(9);} \$\endgroup\$ Commented Jun 11, 2020 at 1:33
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.