最短的程序,连续分配内存


49

编写一个程序,该程序可以永久运行,并在运行时间越长的时候就在堆上分配越来越多的内存,至少要等到达到操作系统对可分配内存量的限制为止。

许多内核实际上不会保留分配给您的内存,除非您将其用于某些事物,所以如果您的程序是C语言或其他低级语言,则必须确保在每个页面上都写一些东西。如果您使用的是解释语言,则可能不必担心这一点。

最短的代码胜出。


13
堆栈溢出是有效的解决方案吗?是否必须泄漏内存或仅分配内存?
小麦巫师

1
@WheatWizard不必泄漏内存,但是必须比释放内存分配更快。
tbodt '16

2
一次我想让程序消耗无限的内存,我无法达到目的。(reduce conj [] (range))(Clojure)上升到737mb,然后停止增长。知道它不会持续上升。它“认为”我想在最后打印整个列表,因此它不应扔掉任何东西。非常沮丧。
Carcigenicate's

14
自我注意:测试前保存代码。引入内存泄漏可能会使IDE崩溃……
steenbergh

1
我认为您应该增加另一个高尔夫挑战,与此类似但又分开,要求程序消耗内存比线性时间函数更快。对于当前的挑战,永远循环并分配一个字节应该没问题。对于您的挑战,这是不够的,但是永远循环并每次使用的内存量加倍都是可以的。
BenGoldberg 2013年

Answers:


46

Funge-98(cfunge),1个字节

9

我本该早些发布,但决定对其进行测试,并且花了一些时间才能使我的计算机恢复可用状态。cfunge将Funge堆栈存储在操作系统的堆上(通过以较小的内存限制运行该程序很容易验证,这是我应该早做的事情!),从而使堆栈无限增长(就像该程序一样,它只是9反复推送;默认情况下,从行尾换行回头的Funge程序将永远分配内存。该程序可能还可以在某些Befunge-93实现中使用。

更有趣的:

"NULL #(4

这是我的第一个想法,并且是一个不依赖于Funge堆栈的无限分配(尽管它也会炸毁Funge堆栈)。首先,该"命令将程序其余部分的副本推入堆栈(它是一个字符串,并且程序自动换行,因此,右引号也用作开引号)。然后N反射(默认情况下没有意义),导致程序向后运行。将"再次运行,并推动程序堆栈-反过来想这一次,与N在堆栈的顶部-然后程序环绕,加载库有4个字母的域名(4(;该NULL库的一部分cfunge的标准库)。NULL定义所有大写字母以进行反射,因此,L反射#跳过返回时的库加载,4将我们不关心的垃圾推入堆栈,并从头开始重复整个程序。鉴于多次加载一个库会产生影响,并且要求该库的命令列表对于该库的每个副本都必须存储一次(这是Funge-98的语义所隐含的),因此它会通过非堆栈存储泄漏内存(相对于语言而不是OS定义“堆”的另一种方法)。


2
我只是要接受这个……
tbodt

该数字是否必须为9?如果是5,也可以吗?
tbodt '16

压入堆栈的所有内容都可以工作(可能0的情况除外;考虑到所涉及的内存已满零,Funge实现或OS可能会找到一种方法来对其进行优化)。我随便选9

22
Unaccepting因为我希望我的信誉仍然是666
tbodt

7
@tbodt不是接受的真正原因。如果您愿意,我会为您的问题-1。然后,当您接受时,您仍然会有703(请注意,您现在有703,而不是666)。
NoOneIsHere


22

Bash + coreutils,5个

要么

红宝石,5岁

`yes`

yes产生无尽的输出。把yes在反引号告诉shell来捕获所有输出,然后执行该输出的命令。Bash将继续为该无休止的字符串分配内存,直到堆用完。当然,结果输出最终将是无效命令,但在此之前我们应该用光内存。

感谢@GB指出这也是红宝石的通配符。


7
我正要编写相同的代码,并将其称为Ruby程序。
GB

1
和perl,我认为。
abligh

18

Python,16个字节

保持嵌套a直到出现错误:

a=0
while 1:a=a,

前几个迭代(如元组)如下所示:

0
(0,)
((0,),)
(((0,),),)

等等等等。


18

> <>(Fish),1个字节

0

在这里尝试!

0 实际上可以代替任何十六进制数1-f。

说明

0在> <>中,只需为鱼游动创建一个1x1代码箱。它会不断0向堆栈中添加一个,然后向右游动,这会循环返回到0,并将其再次添加到堆栈中。这将永远持续下去。


2
现在,我想知道还有多少其他二维语言可以使用。毕竟,大多数它们都是基于堆栈的。

1
几乎可以在Cubix中使用,但是它需要一个前导.(或任何非空白char)将其0移入执行行。
ETHproductions 2016年

1
可在Ouroboros中工作,但不能以相同的方式工作:解释器尝试将其读取0000000...为单个整数文字,并且它所建立的字符串将继续占用更多内存。一个程序可以做到这一点a(无限推10个)。
DLosc

12

Java 101字节

class A{public void finalize(){new A();new A();}public static void main(String[]a){for(new A();;);}}

创建并丢弃对象后,将主程序捕获到一个无限循环中。垃圾收集通过为每个删除的对象创建2个对象来完成泄漏的工作


好吧,因为现在不去了解明显的东西,我感到有点傻,哈哈。我敢说这比我更优雅

1
是的,您的代码通过finalize()@
poke

我认为您可以通过将main替​​换为静态初始值设定项来使其更短
tbodt

只能在
java6

2
哈哈使用垃圾收集器造成泄漏!好主意:)
Mark K Cowan

12

Perl,12个字节

{$"x=9;redo}

在perl中, x运算符在左侧带有一个字符串,在右侧带有一个数字,产生一个重复的字符串。因此"abc" x 3评估为"abcabcabc"

x=操作者变异左边参数,具有重复它的内容多次其右手侧表示的结果置换其左边的变量的内容。

Perl具有许多奇怪的内置变量名称,其中一个是$",其初始值为一个空格。

redo运营商跳转到封闭的开始{}

第一次完成x=运算符时,它将值$"" "“ 更改为" ",这是9位。

第二次x=操作员完成后,将的值更改$"" ",这是81位。

第三次 $"成为一个729字节长的空格字符串。

我认为您可以看到问题所在:)。


你打败我了!而您的则短了三个字节。
加布里埃尔·贝纳米

1
只需在此网站上搜索最小循环即可:)。同样,我最初会$_.=7陷入循环,但是意识到如果我可以使用x=它,它将以更快的速度耗尽内存,然后运行perldoc perlvar以选择合适的东西。
BenGoldberg '16

{$^O++;redo}是一个字节时,短^O一个chr(15)字节。尽管它将以更慢的速度浪费内存-Windows上需要1000000000次迭代才能浪费一个字节。在任何名称以拉丁字母开头的操作系统上均可使用。
Oleg V. Volkov

11

sed,5个字节

打高尔夫球

H;G;D

用法(任何输入都可以)

sed 'H;G;D' <<<""

讲解

#Append a newline to the contents of the hold space, 
#and then append the contents of the pattern space to that of the hold space.
H

#Append a newline to the contents of the pattern space, 
#and then append the contents of the hold space to that of the pattern space. 
G

#Delete text in the pattern space up to the first newline, 
#and restart cycle with the resultant pattern space.
D

屏幕截图

enter image description here

在线尝试!


2
严格来说,这是GNU sed(分号不是标准sed),但是换行符和分号一样好用。
R..16 /

10

Haskell,23个 19字节

main=print$sum[0..]

打印无限列表的总和


这是执行评估的好方法,并且它也非常紧凑。+1
硕果累累

编译器可以很好地在O(1)内存中运行它。在GHC中,sum定义为foldl (+) 0,什么阻止制止严格性分析,以防止重击喷出?您是否运行了优化编译?
尼斯,

@WillNess答案是什么?sum不会事先知道列表是无限的,print总和必须首先进行评估。是的,我与优化编译它
Angs

不会有答案;但计算将在O(1)空间中进行。糟糕,由于默认为Integer,所以数字是无限制的,并且bignum 当前结果占用的内存确实会增加。
尼斯,

1
为了明确起见,我的意思是sum xs = foldl (+) 0 xs像任何命令循环一样,的计算可以在恒定堆栈中运行。foldl' (+) 0 xs当然会。因此,确定内存分配的唯一方法就是bignum临时结果。
尼斯将于

9

C ++(使用g ++编译器),27 23 15字节

感谢Neop帮助我删除4个字节

该解决方案实际上不会泄漏任何内存,因为它会分配堆栈中的所有内容,从而导致堆栈溢出。它只是无限递归的。每次递归都会导致分配一些内存,直到堆栈溢出为止。

main(){main();}

替代解决方案

此解决方案实际上会泄漏内存。

main(){for(;;new int);}

Valgrind输出

这是Valgrind的输出,它在运行时间结束几秒钟后终止了程序。您可以看到它肯定是在泄漏内存。

==2582== LEAK SUMMARY:
==2582==    definitely lost: 15,104,008 bytes in 3,776,002 blocks
==2582==    indirectly lost: 0 bytes in 0 blocks
==2582==      possibly lost: 16 bytes in 4 blocks
==2582==    still reachable: 4 bytes in 1 blocks
==2582==         suppressed: 0 bytes in 0 blocks

3
标题具有误导性;问题是“编写一个永远运行并不断分配内存的程序”。
NobodyNada

哦,当我发送我的邮件时,我还没有意识到您已经提交了答案。
Neop '16

1
@Neop好吧,int 直到我看到你的如此感谢,我才知道你可以省略它!
小麦巫师

2
不是C++,只是它的g ++方言:C ++禁止调用main;C ++需要int main...声明。但是解决方案仍然很简洁:-)
Martin Ba

1
实际上,C ++禁止调用main
R..16 /

9

JAVA, 81 79 78字节

JAVA(热点) 71 70字节

我发布时比其他Java回答短(81个,后来是79个字节):

class A{public static void main(String[]a){String x="1";for(;;)x+=x.intern();}}

如@OlivierGrégoire所建议,可以再保存一个字节:

class A{public static void main(String[]a){for(String x="1";;)x+=x.intern();}}

放置x+=x.intern()为for循环增量将无济于事,因为仍然需要使用分号来结束for语句。

正如@ETHproductions所建议的,也可以使用x+=x作品:

class A{public static void main(String[]a){String x="1";for(;;)x+=x;}}

这也可以从@OlivierGrégoire的技巧中受益:

class A{public static void main(String[]a){for(String x="1";;)x+=x;}}

我对此的唯一担忧是,不能保证在堆上分配数据,因为高效的JVM可以轻松地实现x永远不会逃脱本地功能。使用intern()避免了这种担忧,因为实习字符串最终最终存储在静态字段中。但是,HotSpot确实会生成OutOfMemoryError为该代码,因此我想它还可以。

更新:@Olivier Gregoire还指出,x+=x代码可以运行,StringIndexOutOfBoundsException而不是OOM在有大量内存可用时运行。这是因为Java使用32位int类型来索引数组(而String只是的数组char)。这不会影响x+=x.intern()解决方案,因为后者所需的内存在字符串的长度上是平方的,因此应该按比例分配到2 ^ 62个分配字节。


欢迎来到PPCG!我不太了解Java。如果您这样做会怎样x+=x;
ETHproductions 2016年

您可以通过将x+=x.intern()for循环的最后一个分号放在后面来
删除

好答案。我知道一定要intern穿线,但是我对不安全感到非常满意,并最终确定我停止寻找,哈哈。最初,此问题指定为“内存泄漏”,这就是为什么我不只是做字符串concat答案。

如果您的答案取决于Java的特定实现,并且不一定可以移植到所有Java实现中,则可以将信息放在标题中(例如# Java (HotSpot), 71 bytes)。这样,您就不必担心解决方案可能会作弊;特定于实现的程序不仅在打高尔夫球中很常见,而且在更广泛的编程世界中也很常见,并且只要您知道自己在做什么,有时可能比可移植程序更适合某个人,例如关闭脚本。

1
哼... x+=x;不会耗尽整个内存。有了64 GB,我得到了StringIndexOutOfBoundsException,而不是OOM。随着.intern()我仍然得到OOM。
OlivierGrégoire'16



6

Python 3,16个字节

i=9
while 1:i*=i

这是由于Python 3对整数大小没有限制;取而代之的是,整数会占用系统可以处理的内存(如果我对这的理解有误,请纠正我)。


标题暗示应该泄漏内存。但这实际上并不会泄漏内存。作者可能应该澄清一下。
小麦巫师

6

Rust,46个字节

fn main(){loop{std::mem::forget(Box::new(1))}}

注意这个Rust程序的有趣之处,泄漏堆分配直到内存不足?

没错,没有不安全的障碍。Rust以安全代码保证了内存安全(不读取未初始化的数据,释放后读取,释放两次等),但是内存泄漏被认为是绝对安全的。甚至还有一个显式函数使编译器忘记RAII清除超出范围的变量,我在这里使用它。


6

TI-83十六进制组件,7字节

PROGRAM:M
:AsmPrgm
:EF6A4E
:C3959D
:C9

无限期创建appvar,直到ERR:MEMORYOS抛出。用运行Asm(prgmM)。我将每对十六进制数字都视为一个字节。


6

Python,8个字节

2**9**99

OP已经允许不技术上运行“永远”程序的技术细节,但分配更多的内存比任何一台计算机可能可能处理。这不是一个googolplex(可能是10**10**10011个字节),但是天真的,该数的对数以2为底

>>> 9**99.
2.9512665430652752e+94

即10 ^ 94位代表它。WolframAlpha认为它比深网大10 ^ 76(请记住,宇宙中大约有10 ^ 80个原子)。

为什么问2而不是9?差别不大(使用9只会将位数增加的倍数log2(9) = 3.2,甚至不会改变指数)。但另一方面,由于计算更简单,因此使用2可以使程序运行更快。这意味着它会立即填满内存,而不是9版本,这会由于需要进行计算而花费更长的时间。没必要,但是如果您想“测试”这个(我确实做了),那就很好了。


5

果冻3 2 字节

-1字节的感谢丹尼斯(W换行)

链接(即函数或方法)也可以用作完整程序,将其输入递归包装到列表中。

输入从零开始,因此第一遍创建列表[0]
,第二遍进行此操作[[0]]
,第三遍进行此操作[[[0]]]
,依此类推...


之前的3个BYTER,泄漏速度更快:

;Ẇß

将其输入的所有非空连续子列表递归连接到其输入。
[0]-> [0,[0]]-> [0,[0],[0],[[0]],[0,[0]]]依此类推...


如果我正确理解规则,‘ß应该足够。
丹尼斯,

确实做到了“连续分配内存”(想想Python为小整数保持不变的分配)吗?
乔纳森·艾伦

1
很公平。应该仍然符合要求。
丹尼斯,

5

Java 7,106字节

class A{public void finalize(){for(;;)Thread.yield();}public static void main(String[]a){for(;;)new A();}}

少打高尔夫球

class A{
    @Override
    public void finalize(){
        for(;;) {
            Thread.yield();
        }
    }
    public static void main(String[]a){
        for(;;){
            new A();
        }
    }
}

finalize当垃圾回收确定不再有对该对象的引用时,垃圾回收器将在对象上调用该方法。我只是简单地重新定义了该方法以使其永远循环,所以垃圾收集器从不真正释放内存。在main循环中,我创建了永远不会清除的新对象,因此最终将耗尽所有可用内存。

Java 7(有趣的替代方法),216字节

import sun.misc.*;class A{public static void main(String[]a)throws Exception{java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(1>0);for(;;)((Unsafe)f.get(null)).allocateMemory(9);}}

少打高尔夫球

import sun.misc.*;
class A{
    public static void main(String[]a)throws Exception{
        java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe u = (Unsafe)f.get(null);
        for(;;) {
            u.allocateMemory(9);
        }
    }
}

这比其他任何事情都有趣。该答案利用Unsafe了未公开的内部API Sun库。您可能需要更改编译器设置,以允许使用受限的API。Unsafe.allocateMemory分配指定数量的字节(不进行任何边界检查),该字节不在堆上,也不在Java的垃圾收集器管理下,因此该内存将一直存在,直到您调用Unsafe.freeMemory或jvm内存不足为止。


1
想知道我是否会在这里看到Java。
魔术八达通Ur

如果垃圾回收器在单独的线程中运行,第一个不是唯一有效吗?
tbodt

@tbodt是的,但是我不认为这不是事实。垃圾收集发生在称为垃圾收集器的守护进程线程中

@Poke保证吗?如果不是,答案仍然是正确的,但您应该澄清一下,它仅在垃圾收集器在其自己的线程中运行时才有效
tbodt

@tbodt我这样认为的,但我不确定。

5

Haskell,24个字节

f x=f$x*x
main=pure$!f 9

Haskell的主要问题是克服惰性。main需要具有某种IO类型,因此简单地调用main=f 9将不起作用。使用main=pure(f 9)将类型f 9提升为IO类型。但是,使用like main=pure 9这样的构造不会做任何事情,不会在任何9地方返回或显示,而是将其丢弃,因此无需评估的参数pure,因此main=pure(f 9)不会导致f未调用的任何内存被分配。为了执行评估,需要$!操作员。它只是将函数应用于自变量,但首先评估自变量。因此,使用main=pure$!f 9评估f并因此连续分配更多的内存。


编译后,运行时将检测到循环并中断执行
Angs 2013年

@Angs我在Windows上用ghc进行编译,并且它一直愉快地分配内存...我将其停止在3GB。
Laikoni '16

也使用f x=f x作品吧?(−2字节)
wchargin '16

@wchargin我不这样认为,它f x=f x会产生无限循环,但不会分配新的内存。
Laikoni '16

不错,通过bignum计算会导致内存耗尽!f!x=x*f(x*x)应该使其经过优化。
尼斯(Ness Ness)2016年

5

dc,7个字节

[ddx]dx

[ddx]将包含“ ddx”的字符串压入堆栈。dx复制它,然后将其作为代码执行(在堆栈上保留一个副本)。执行时,它会制作两个副本,然后执行一个副本,每次都在堆栈上再保留一个副本。


等待,如果可以并行运行,这将成倍地分配内存吗?
HyperNeutrino '16

5

Haskell(使用ghc 8.0.1),11个字节

m@main=m>>m

非尾递归。main称自己,然后又称自己。


这是分配在堆还是堆栈上?(我可以相信;它很可能取决于使用的Haskell编译器。)

1
@ ais523:这取决于。Haskell没有调用堆栈。运行时系统RTS具有用于模式匹配的存储区,也称为“堆栈”。该堆栈在堆上分配。老实说,我不知道这是怎么回事,因为与Stack space overflow: current size 33624 bytes.OS报告的6G总内存相比,该程序以33k 失败似乎很低。
nimi 2016年

1
@ ais523:ghc错误消息的内存信息中似乎有一个错误,因此很难说出到底发生了什么。
nimi 2016年

在Ubuntu上的GHC 7.10.3上编译,即使禁用了优化,这似乎也需要恒定的内存量
Angs 2016年

@Angs:嗯,我在MacOS上使用ghc 8.0.1。我会编辑。
NIMI

5

C(linux),23个字节

main(){while(sbrk(9));}

sbrk()将数据段的顶部增加给定的字节数,从而有效地增加分配给程序的内存量-至少如在输出VIRT字段中报告的那样top。这仅在Linux上有效-macOS实施显然是一种仿真,最多只能分配4MB。


所以有一个更一般的答案:

C,25个字节

main(){while(malloc(9));}

我在macOS活动监视器上观看了它。它一路上升到大约48GB,然后该进程最终收到了SIGKILL信号。FWIW我的macbook pro有16GB。据报告,大多数使用的内存已压缩。

请注意,该问题实际上要求将每个分配写入其中,此处未明确发生。但是,重要的是要注意,对于每个malloc(9)调用,分配的不仅仅是9个用户请求的字节。对于分配的每个块,都会有一个malloc标头,该标头也是从堆上的某个位置分配的,必须由malloc()内部函数写入。


使用malloc,由于malloc不会初始化任何内容,因此您不会直接在内存中写入数据。仅分配内存是因为malloc需要一些内部存储来进行内存管理。因此,答案并不是真正的标准,但我想无论如何它都能在任何地方起作用。
Antzi '16

@Antzi是的。但是,我认为这仍然可行,因为即使在写入用户内存之前可能实际上并未对其进行分配,每个malloc()ed块仍必须具有自己的实际分配空间。这适用于macOS和Ubuntu。
数字创伤

每页所写问题的条件是没有意义的;即使您想假设某个操作系统没有进行正确的提交记帐,无论实现细节如何,每次分配都需要簿记数量为非零。无论它是否与分配相邻(是否要触摸页面),它最终都会(任意)非零数据消耗任意数量的内存用于簿记。
R.,

您可以将它缩小为1个字节main(){main(malloc(9));},但是为了不引起堆栈溢出,它需要进行尾部调用优化,而gcc似乎不想在main... 上这样做。
R.,

如果将malloc(9)替换为calloc(9,9),则将为9个字节的块的9个实例分配足够的内存(根据对齐方式,介于81和144个字节之间。但是,更重要的是,calloc( )将对内存块进行零填充,强制底层操作系统为其分配存储空间
CSM

5

Perl,4个字节

do$0

在当前的解释器中执行自身。完成后,执行返回到调用脚本,该脚本需要调用堆栈。


不错,简短,尽管它不会像我一样迅速浪费内存。
BenGoldberg 2013年

4

球拍,13个字节

(let l()(l)1)

我不确定我的答案是否属于这个问题。请让我知道是否应该删除此答案。


您能解释一下它是如何工作的吗?
tbodt

1
哦,所以它被定义l为一个执行非tailcall递归的函数。我会说很重要。
tbodt

@tbodt是的,您说对了
Winny

4

JavaScript的22 21 17 16 15字节

for(a=0;;)a=[a]

通过将列表包装在另一个列表中,节省了4个字节,就像@Jonathan Allan的Jelly答案一样。

@ETHProductions节省了1个字节

备用解决方案15字节(仅适用于正确的尾部调用)

f=a=>f([a]);f()

1
在ES6的第二个示例中,您不能这样做f=_=>f();f()吗?12个字节

@bitten我不确定。如果要浪费调用堆栈,那么没有适当的尾部调用的方法将是正确的选择。使用TCO,我认为不会有任何内存泄漏,会吗?
Lmis

两者都给我带来了麻烦。我不是很熟悉尾叫功能,所以我无法对此发表评论。
咬伤

1
啊,我知道了,我不确定你的内存泄漏情况
咬伤

1
您可以删除a=0。第一次迭代将导致a=[undefined]
Florent

4

Ruby,11个字节

loop{$*<<9}

保持推9$*,这是一个数组最初保持在命令行参数Ruby进程。


4

05AB1E,2个字节

[A

在线尝试! 只会继续努力abcdefghijklmnopqrstuvwyxz入栈中,直到永恒。

所有可能的2字节解决方案:

[  # Infinite loop.
 A # Push alphabet.
 0 # Push 0.
 1 # Push 1.
 2 # Push 2.
 3 # Push 3.
 4 # Push 4.
 5 # Push 5.
 6 # Push 6.
 7 # Push 7.
 8 # Push 8.
 9 # Push 9.
 T # Push 10.
 X # Push 1.
 Y # Push 2.
 ® # Push -1.
 ¶ # Push \n.
 º # Push len(stack) > 0, so 0 once then 1 for eternity.
 ð # Push a space.
 õ # Push an empty string.
 ¾ # Push 0.
 ¯ # Push [].
 M # Push -inf.
 ) # Wrap current stack in an array.

很彻底!真好
timothymh '16

3

Python,35个字节

def f(a=[]):a.append(a)
while 1:f()

a 永远不会释放,只会变得更大,直到您击中 MemoryError

您可以在Python Tutor上查看执行。


1
你可以a+=a,吗?
Cyoce

不需要功能,这就是
追求

@ Flp.Tkc问题写完后,我写了这个答案,如果它是当前格式的,我会做的(+-个字符)。
Noelkd 9:49

3

TI-BASIC,8

:Lbl A
:While 1
:Goto A

(所有1字节令牌和两个换行符)

这会不断泄漏内存,因为结构化的控制流(如While预期会被ans关闭)End会将某些内容推入堆栈(而不是OS堆栈,即堆内存中的单独堆栈)以进行跟踪。但是在这里,我们习惯于Goto离开循环(因此不End执行任何操作以将其从堆栈中删除),While再次可见,将其再次推入,等等。因此,它一直在推入它们,直到您得到ERR:MEMORY

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.