
Does memory leak? (1995) - rot25
https://groups.google.com/forum/message/raw?msg=comp.lang.ada/E9bNCvDQ12k/1tezW24ZxdAJ
======
derefr
Erlang has a parameter called initial_heap_size. Each new actor-process in
Erlang gets its own isolated heap, for which it does its own garbage-
collection on its own execution thread. This initial_heap_size parameter
determines how large each newly-spawned actor’s heap will be.

Why would you tune it? Because, if you set it high enough, then for all your
_short-lived_ actors, memory allocation will become a no-op (= bump
allocation), and the actor will never experience enough memory-pressure to
trigger a garbage-collection pass, before the actor exits and the entire
process heap can be deallocated as a block. The actor will just “leak” memory
onto its heap, and then exit, never having had to spend time accounting for
it.

This is also done in many video games, where there is a per-frame temporaries
heap that has its free pointer reset at the start of each frame. Rather than
individually garbage-collecting these values, they can all just be invalidated
at once at the end of the frame.

The usual name for such “heaps you pre-allocate to a capacity you’ve tuned to
ensure you will never run out of, and then deallocate as a whole later on” is
a _memory arena_. See [https://en.wikipedia.org/wiki/Region-
based_memory_management](https://en.wikipedia.org/wiki/Region-
based_memory_management) for more examples of memory arenas.

~~~
dahart
The games and GPU apps I’ve worked on use memory pools for small allocations,
where there will be individual pools for all, say, 1-16 byte allocations,
16-64 byte allocations, 64-256 byte allocations, etc. (Sizes just for
illustration, not necessarily realistic). The pool sizes always get tuned over
time to match the approximate high water mark of the application.

I think pools and arenas mean pretty much the same thing.
[https://en.wikipedia.org/wiki/Memory_pool](https://en.wikipedia.org/wiki/Memory_pool)
I’ve mostly heard this discussed in terms of pools, but I wonder if there’s a
subtle difference, or if there’s a historical reason arena is popular in some
circles and pool in others...?

I haven’t personally see a per-frame heap while working in console games, even
though games I’ve worked on probably had one, or something like it. Techniques
that I did see and are super-common are fixed maximum-size allocations: just
pre-allocate all the memory you’ll ever need for some feature and never let it
go; stack allocations sometimes with alloca(); and helper functions/classes
that put something on the stack for the lifetime of a particular scope.

~~~
monocasa
I've seen Jason Gregory talk about per frame arenas in Game Engine
Architecture as a fundamental piece of how the Naughty Dog engines tend to
work.

Totally agreed that they aren't required for shipping great console games (and
they're really hard to use effectively in C++ since you're pretty much
guaranteed to have hanging references if you don't have ascetic levels of
discipline). This is mainly just meant as a "here's an example of how they can
be used and are by at least one shop".

~~~
foota
Seems like this could be handled with a wrapper type with runtime checks
during debug?

Like make any pointer to the per frame allocation be a TempPointer or
something and then assert they're all gone with a static count variable of
them? Then you just have to be cautious whenever you pass a reference to one
or convert to a raw pointer.

I don't think this would be too awful for performance in debug builds.

~~~
monocasa
Yeah, or a generation system where the pointer holds a frame count too that's
asserted on deref.

The point though is that it's a step back still from shared_ptr/unique_ptr by
becoming a runtime check instead of compile time.

------
jakeinspace
As somebody working on embedded software for aerospace, I'm surprised this
missile system even had dynamic memory allocation. My entire organization
keeps flight-critical code fully statically allocated.

~~~
giu
I'm always fascinated about software running on hardware-restricted systems
like planes, space shuttles, and so on.

Where can someone (i.e., in my case a software engineer who's working with
Kotlin but has used C++ in his past) read more about modern approaches to
writing embedded software for such systems?

I'm asking for one because I'm curious by nature and additionally because I
simply take the garbage collector for granted nowadays.

Thanks in advance for any pointers (no pun intended)!

~~~
0xffff2
The embedded world is _very_ slow to change, so you can read about "modern
approaches" (i.e. approaches used today) in any book about embedded
programming written in the last 30 years.

I currently work on spacecraft flight software and the only real advance on
this project over something like the space shuttle that I can point to is that
we're trying out some continuous integration on this project. We would like to
use a lot of modern C++ features, but the compiler for our flight hardware
platform is GCC 4.1 (upgrading to GCC 4.3 soon if we're lucky).

~~~
rowanG077
I find it interesting that such critical code is written in C. Why not use
something with a lot more (easily)statically provable properties. Like Rust or
Agda?

~~~
amw-zero
You’ll find that for very serious, industrial applications, a conservative
mindset prevails. C may not be trendy at the moment, but it powers the
computing world. Its shortcomings are also extremely well known and also
statically analyzable.

Also, think about when flight software started being written. Was Rust an
option? And once it came out, do you expect that programmers who are
responsible for millions of people’s lives to drop their decades of tested
code and development practices to make what is a bet on what is still a new
language?

What I find interesting is this mindset. My conservativeness on a project is
directly proportional to its importance / criticality, and I can’t think of
anything more important or critical than software that runs on a commercial
airplane. C is a small, very well understood language. Of course it gives you
nothing in terms of automatic memory safety, but that is one tradeoff in the
list of hundreds of other dimensions.

When building “important” things it’s important to think about tradeoffs,
identify your biases, and make a choice that’s best for the project and the
people that the choice will affect. If you told me that the moment anyone dies
as a result of my software I would have to be killed, I would make sure to use
the most tried-and-true tools available to me.

~~~
AtlasBarfed
You're advocating throwing baby out with bathwater.

Rust interops with C seamlessly, doesn't it? You don't have to throw out good
code to use a better language or framework.

C may be statically analyzable to some degree, but if Rust's multithreading is
truly provable, then new code can be Rust and of course still use the tried
and true C libraries.

Disclaimer: I still haven't actually learned any Rust, so my logic is CIO-
level of potential ignorance.

~~~
wallacoloo
> Rust interops with C seamlessly, doesn't it?

From someone who works in a mixed C + Rust codebase daily (Something like 2-3M
lines of C and 100k lines of Rust), yes and no. They're pretty much ABI
compatible, so it's trivial to make calls across the FFI boundary. But each
language has its own set of different guarantees it provides _and assumes_ ,
so it's easy to violate one of those guarantees when crossing a FFI boundary
and triggering UB which can stay hidden for months.

One of them is mutability: in C we have some objects which are internally
synchronized. If you call an operation on them, either it operates atomically,
or it takes a lock, does the operation, and then releases the lock. In Rust,
this is termed "interior mutability" and as such these operations would take
non-mutable references. But when you actually try that, and make a non-mutable
variable in Rust which holds onto this C type, and start calling C methods on
it, you run into UB even though it seems like you're using the "right"
mutability concepts in each language. On the rust side, you need to encase the
C struct inside of a UnsafeCell before calling any methods on it, which
becomes not really possible if that synchronized C struct is a member of
another C struct. [1]

Another one, although it depends on how exactly you've chosen to implement
slices in C since they aren't native: in our C code we pass around buffer
slices as (pointer, len) pairs. That looks just like a &[T] slice to Rust. So
we convert those types when we cross the FFI boundary. Only, they offer
different guarantees: on the C side, the guarantee is generally that it's safe
to dereference anything within bounds of the slice. On the rust side, it's
that, _plus_ the pointer must point to a valid region of memory (non-null)
even if the slice is empty. It's just similar enough that it's easy to
overlook and trigger UB by creating an invalid Rust slice from a (NULL, 0)
slice in C (which might be more common than you think because so many things
are default-initialized. a vector type which isn't populated with data might
naturally have cap=0, size=0, buf=NULL).

So yeah, in theory C + Rust get along well and in practice you're good 99+% of
the time. But there are enough subtleties that if you're working on something
mission critical you gotta be real careful when mixing the languages.

[1]
[https://www.reddit.com/r/rust/comments/f3ekb8/some_nuances_o...](https://www.reddit.com/r/rust/comments/f3ekb8/some_nuances_of_undefined_behavior_in_rust/)

~~~
a1369209993
> On the rust side, it's that, _plus_ the pointer must point to a valid region
> of memory (non-null) even if the slice is empty.

Do you have a citation for that, because it _seems_ obviously wrong[0] (since
the slice points to zero bytes of memory) and I'm having trouble coming up
with any situation that would justify it (except possibly using a NULL pointer
to indicate the Nothing case of a Maybe<Slice> datum)?

0: by which I mean that Rust is wrong to require that, not that you're wrong
about what Rust requires.

~~~
wallacoloo
Well the docs have this to say [1]:

`data` must be non-null and aligned even for zero-length slices. One reason
for this is that enum layout optimizations may rely on references (including
slices of any length) being aligned and non-null to distinguish them from
other data. You can obtain a pointer that is usable as data for zero-length
slices using NonNull::dangling().

So yes, this requirement allows optimizations like having Option<&[T]> be the
same size as &[T] (I just tested and this is the case today: both are the same
size).

I'm not convinced that it's "wrong", though. If you want to be able to support
slices of zero elements (without using an option/maybe type) you have to put
_something_ in the pointer field. C generally chooses NULL, Rust happens to
choose a different value. But they're both somewhat arbitrary values. It's not
immediately obvious to me that one is a better choice than the other.

[1] [https://doc.rust-
lang.org/std/slice/fn.from_raw_parts.html](https://doc.rust-
lang.org/std/slice/fn.from_raw_parts.html)

~~~
a1369209993
> [1] [https://doc.rust-
> lang.org/std/slice/fn.from_raw_parts.html](https://doc.rust-
> lang.org/std/slice/fn.from_raw_parts.html)

Thanks.

> having Option<&[T]> be the same size as &[T]

That is literally what I mentioned as a possible reason ("except possibly
..."), but what I overlooked was that you could take a mutable reference to
the &[T] inside a Option<&[T]>, then store a valid &[T] into it - if NULL is
allowed, you effectively mutated the discriminant of a enum when you have
active references to its fields, violating _some_ aspect of type/memory
safety, even I'm not sure _which_.

> C generally chooses NULL, Rust happens to choose a different value.

It's not about what pointer value the langauge chooses when it's asked to
create a zero-length slice, it's about whether the language _accepts_ a NULL
pointer in a zero-length slice it finds lying around somewhere.

------
zdw
Another "works because it's in a missile and only has to run for as short
time" story:

Electronics components for trajectory tracking and guidance for a particular
missile weren't running fast enough, namely the older CPU that the software
was targeting. The solution to this was to overclock the CPU by double, and
redirect a tiny amount of the liquid oxygen that happened also to be used in
the propellent system to cool down the electronics.

This apparently worked fine - by the time the missile ran out of LOX and the
electronics burned themselves out, it was going so fast on a ballistic
trajectory that it couldn't be reasonably steered anyway.

The telemetry for the self destruct was on a different system that wasn't
overclocked, in case of problems with the missile.

------
Out_of_Characte
What an interesting concept. Good programmers always consider certain
behaviours to be wrong. Memory 'leaks' being one of them. But this real
application of purposefully not managing memory is also an interesting thought
exercise. However counter intuitive, a memory leak in this case might be the
most optimal solution in this problem space. I just never thought I would have
to think of an object's lifetime in such a literal sense.

Edit; ofcouse HN reacts pedantic when I claim good programmers always consider
memory leaks wrong. Do I really need to specify the obvious every time?

~~~
blattimwind
Cleaning up memory is an antipattern for many _tools_ , especially of the
EVA/IPO model (input-process-output). For example, cp(1) in preserve hard
links mode has to keep track of things in a table; cleaning it up at the end
of the operation is a waste of time. Someone "fixed" the leak to make valgrind
happy and by doing so introduced a performance regression. Another example
might be a compiler; it's pointless to deallocate all your structures manually
before calling exit(). The kernel throwing away your address space is
infinitely faster than you chasing every pointer you ever created down and
then having the kernel throw away your address space. The situation is quite
different of course if you are libcompiler.

~~~
atq2119
> Another example might be a compiler; it's pointless to deallocate all your
> structures manually before calling exit().

And now the compiler can no longer be embedded into another application, e.g.
an IDE.

It's a reasonably pragmatic way of thinking, but beware the consequences. One
benefit of working with custom allocators is that you can have the best of
both worlds. Unfortunately, custom allocators are clumsy to work with.

~~~
badsectoracula
Solve the problem you have now, not the problem you may not have later. You
can worry about that when the time comes, if it ever comes.

In the case of compiler, one solution would be to replace all calls to
`malloc` with something like `ccalloc` that simply returns pieces of a
`realloc`'d buffer which is freed after the in-IDE compiler has finished
compiling.

------
simias
I think it's a bad mindset to leak resources even when it doesn't effectively
matter. In non-garbage collected languages especially, because it's important
to keep in mind who owns what and for how long. It also makes refactoring
easier because leaked resources effectively become some sort of implicit
global state you need to keep track of. If a function that was originally
called only once at startup is not called repeatedly and it turns out that it
leaks some memory every time you know have a problem.

In this case I assume that a massive amount of testing mitigates these issues
however.

~~~
mannykannot
I think you are conflating two issues: while one should understand who owns
what and for how long, it does not follow that one should always free
resources even when it is not necessary, if doing so adds complexity and
therefore more things to go wrong, or if it makes things slower than optimal.

In this particular case, correctness was not primarily assured by a massive
amount of testing (though that may have been done), but by a rigorous static
analysis.

~~~
jldugger
I feel like I've read about some rocket launch failures that were caused in
part by launch delays leading to overflow and sign flipping, but can't find it
now =/

It may be unwise to overide static analysis (a leak is found) with hueristics
(the program won't run long enough to matter)

~~~
mannykannot
It is not just a heuristic if you have hard upper bounds on the things that
matter - in that case, it is static analysis. A missile has a limited, and
well-defined, fuel supply.

In the case of memory management, it is not enough to just free it after use;
you need to ensure that you have sufficient contiguous memory for each
allocation. If you decide to go with a memory-compaction scheme, you have to
be sure it never introduces excessive latency. It seems quite possible that to
guarantee a reallocation scheme always works, you have to do more analysis
than you would for a no-reallocation scheme with more memory available.

~~~
jldugger
This depends entirely on the mode of operation which I suspect neither of us
know in great detail; if in any circumstance the runtime of the program is not
tied to expenditure of fuel you have literal ticking time bomb.

Ideally we'd be able to tie such assertions into a unified static analysis
tool, rather than having humans evaluate conflicting analyses. And god forbid
the hardware parameters ever change, because now you need to re-evaluate every
such decision, even the ones nobody documented. Case in point: Arianne 5 (not
exactly my original scenario, but exactly this one -- 64bit -> 16 bit overflow
caused a variety of downstream effects ending in mission failure).

~~~
mannykannot
Well, yes, I already explained that it depended on circumstances, and just let
me add that I would bet the engineer quoted in the article (explaining that
the memory leaks were a non-issue) knew much more about the specifics than
either of us.

The Ariane 5 issue is not, of course, a memory leak or other rescource-
release-and-reuse issue. It is a cautionary tale about assumptions (such as
the article's authors assumption that memory leaks are always bad.)

------
lmilcin
I once worked on an application which if failed even once meant considerable
loss for the company including possible closure.

By design, there was no memory management. The memory was only ever allocated
at the start and never de-allocated. All algorithms were implemented around
the concept of everything being a static buffer of infinite lifetime.

It was not possible to spring a memory leak.

~~~
conro1108
This sounds fascinating, could you elaborate any on why a single failure of
this application would be so catastrophic?

~~~
lmilcin
I can't discuss this particular application.

But there are whole classes of applications that are also mission critical --
an example might be software driving your car or operating dangerous chemical
processes.

For automotive industry there are MISRA standards which we used to guide our
development process amongst other ideas from NASA and Boeing (yeah, I know...
it was some time ago)

------
crawshaw
This is an example of garbage collection being more CPU efficient than manual
memory management.

It has limited application, but there is a more common variant: let process
exit clean up the heap. You can use an efficient bump allocator for `malloc`
and make `free` a no-op.

~~~
acqq
There was also a variant of it with the hard drives: building Windows produced
a huge amount of object files, so the trick used was to use a whole hard disk
(or a partition) for that. Before the next rebuild, deleting all the files
would took far more time than a "quick" reformatting of the whole hard disk,
so the later was used.

(I am unable to find a link that talks about that, however).

In general, throwing away at once the set of the things together with the
structures that maintain it is always faster than throwing away every item one
by one while maintaining the consistency of the structures, in spite of the
knowledge that all that is not needed at the end.

An example of arenas in C: "Fast Allocation and Deallocation of Memory Based
on Object Lifetimes", Hanson, 1988:

ftp://ftp.cs.princeton.edu/techreports/1988/191.pdf

~~~
GordonS
That's quite a clever solution, I doubt I would have thought of that!

Windows has always been my daily drivers, and I really do like it. But I wish
deleting lots of files would be much, much faster. You've got time to make a
cup of coffee if you need to delete a node_modules folder...

~~~
acqq
> I wish deleting lots of files would be much, much faster. You've got time to
> make a cup of coffee if you need to delete a node_modules folder

The example I gave was for the old times when people had much less RAM and the
disks had to move physical heads to access different areas. Now with the SSDs
you shouldn't be able to experience it _that_ bad (at least when using lower
level approaches). How do you start that action? Do you use GUI? Are the files
"deleted" to the recycle bin? The fastest way is to do it is "low level" i.e.
without moving the files to the recycle bin, and without some GUI that is in
any way suboptimal (I have almost never used Windows Explorer so I don't know
if it has some additional inefficiencies).

[https://superuser.com/questions/19762/mass-deleting-files-
in...](https://superuser.com/questions/19762/mass-deleting-files-in-
windows/289399#289399)

~~~
GordonS
Even with an SSD, it's still bad. Much better than the several minutes it used
to take with an HDD, but still annoying.

I just tried deleting a node_modules folder with 18,500 files in it, hosted on
an NVMe drive. Deleting from Windows Explorer, it took 20s.

But then I tried `rmdir /s /q` from your SU link - 4s! I remember trying
tricks like this back with an HDD, but don't remember it having such a
dramatic impact.

~~~
acqq
>>> You've got time to make a cup of coffee if you need to delete a
node_modules folder...

> Deleting from Windows Explorer, it took 20s.

> `rmdir /s /q` from your SU link - 4s

OK, so you saw that your scenarios could run much better, especially if
Windows Explorer is avoided. But in Explorer, is that time you measured with
deleting to the Recycle Bin or with the Shift Delete (which deletes
irreversibly but can be faster)?

Additionally, I'd guess you don't have to wait at all (i.e. you can reduce it
to 0 seconds) if you first rename the folder and than start deleting that
renamed one and let it doing that in the background while continuing with your
work -- e.g. if you want to create the new content in the original location
it's immediately free after the rename, and the rename is practically
immediate.

~~~
GordonS
I pretty much exclusively use SHIFT-DEL (which has once or twice resulted in
bad times!).

I didn't think about renaming then deleting - that's quite a nice workaround!

------
LucaSas
This pops up again from time to time, I think what people should take away
from this is that garbage collection is not just what you see in Java and
other high level languages.

There are a lot of strategies to apply garbage collection and they are often
used in low level systems too like per-frame temporary arenas in games or in
short lived programs that just allocate and never free.

~~~
asveikau
Once you set a limit like this, though, it's brittle, and your code becomes
less maintainable or flexible in the face of change. That is why a general
purpose strategy is good to use.

------
andreareina
"Git is a really great set of commands and it does things like malloc();
malloc(); malloc(); exit();"

[https://www.youtube.com/watch?v=dBSHLb1B8sw&t=113](https://www.youtube.com/watch?v=dBSHLb1B8sw&t=113)

~~~
jldugger
And that really bit hard when you wanted to start running git webservers. All
the lib code was designed to exit upon completion with no GC, and now you're
running multiple read queries per second with no free(). oops.

------
GordonS
A bit OT, but I wonder how I'd feel if I was offered a job working on software
for missiles.

I'm sure the technical challenge would be immensely interesting, and I could
tell myself that I cared more about accuracy and correctness than other
potential hires... but from a moral standpoint, I don't think I could bring
myself to do it.

I realise of course that the military uses all sorts of software, including
line of business apps, and indeed several military organisations use the B2B
security software that my microISV sells, but I think it's very different to
directly working on software for killing machines.

~~~
jmpman
Straight out of college, I was offered a job writing software for missiles.
Extremely interesting area, working for my adjunct professor’s team, who I
highly admired and whose class was the best of my college career. The pay was
on par with all my other offers. I didn’t accept for two reasons.

First, I logically agreed that the missiles were supporting our armed services
and I believed that our government was generally on the right side of history
and needed the best technology to continue defending our freedoms. However, a
job, when executed with passion, becomes a very defining core of your
identity. I didn’t want death and destruction as my core. I support and admire
my college friends who did accept such jobs, but it just wasn’t for me.

Second, I had interned at a government contractor, (not the missile
manufacturer), and what I saw deeply disturbed me. I came on to a project
which was 5 years into a 3 year schedule, and not expected to ship for another
2 years. Shocked, I asked my team lead “Why didn’t the government just cancel
the contract and assign the work to another company?”, her reply, “If they did
that, the product likely wouldn’t be delivered in under two years, so they
stick with us”. I understood that this mentality was pervasive, and would
ultimately become part of me, if I continued to work for that company. That
mentality was completely unacceptable in the competitive commercial world, and
I feared the complacency which would infect me and not prepare me for the
eventual time when I’d need to look for a job outside that company. As a
graduating senior, I attended our college job fair, and when speaking with
another (non missile) government contractor, I told the recruiter that I was
hesitant working for a his company because I thought it wouldn’t keep me as
competitive throughout my career. I repeated the story from my internship, and
asked if I’d find the same mentality at his company. His face dropped the
cheerful recruiter facade, when he pulled me aside and sternly instructed “You
should never repeat that story”. I took that as an overwhelming “yes”. So, my
concern was that working for this missile manufacturer, this government
contractor mentality would work its way into their company (if it hadn’t
already), and it would be bad for my long term career. I wanted to remain
competitive on a global commercial scale, without relying upon government
support.

~~~
newscracker
_> I came on to a project which was 5 years into a 3 year schedule, and not
expected to ship for another 2 years. Shocked, I asked my team lead “Why
didn’t the government just cancel the contract and assign the work to another
company?”, her reply, “If they did that, the product likely wouldn’t be
delivered in under two years, so they stick with us”. I understood that this
mentality was pervasive, and would ultimately become part of me, if I
continued to work for that company. That mentality was completely unacceptable
in the competitive commercial world, and I feared the complacency which would
infect me and not prepare me for the eventual time when I’d need to look for a
job outside that company._

Software for any system is complex. And it’s quite common for almost every
software project to be late on schedule. The Triple Constraint — “schedule,
quality, cost: pick any two” doesn’t even fit software engineering in any kind
of serious endeavor because it’s mostly a “pick one” scenario.

If you’ve worked on projects where all these three were met with the initial
projections, then whoever is estimating those has really made sure that
they’ve added massive buffers on cost and time or the project is too trivial
for a one person team to do in a month or two.

The entire reason Agile came up as a methodology was in recognizing that
requirements change all the time, and that the “change is the only constant”
refrain should be taken in stride to adapt how teams work.

~~~
AtlasBarfed
I vehemently and violently disagree!

The average project achieves 1.5 of the triples.

Here are the true constraints though:

\- Schedule \- Meets Requirements \- Cost \- Process \- Usefulness/Polish

Yes, usefulness and meets requirements aren't the same thing, and anyone who
has done the madness of large scale enterprise software will be nodding their
heads.

What really bogs down most software projects is that "quality" means different
things to different actors in projects. Project Managers want to follow
process and meet political goals. Users want usefulness, polish, and
efficiency. Directors/management want requirements fulfilled they dictate
(often reporting and other junk that don't add to ROI).

And that I like to say "pick two"

------
tyingq
Until the cruise missile shop down the hall decides to reuse your controller.

~~~
gameswithgo
If all software is built to protect against all possible future anticipated
use cases, your software will take longer to make, perform worse, and be more
likely to have bugs.

If all software is built only to solve the problem at hand, it will take less
time to develop, be less likely to have bugs, and perform better.

It isn't clear that coding for reuse is going to get you a net win, especially
since computing platforms, the actual hardware, is always evolving, such that
reusing code some years later can become sub-optimal for that reason alone.

~~~
eru
There's a middle ground. Eg the classic Unix 'cat' (ignoring all the command
line switches) does something really simple and re-usable, so it makes sense
to make sure it does the Right Thing in all situations.

~~~
thaumasiotes
I mean, 'cat' does something so simple (apply the identity function to the
input) that it has no need to be reusable because there's no point using it in
the first place. If you have input, processing it with cat just means you
wasted your time to produce something you already had.

~~~
derefr
The point of cat(1), short for _concatenate_ , is to feed a pipeline multiple
_concatenated_ files as input, whereas shell stdin redirection only allows you
to feed a shell a single file as input.

This is actually highly flexible, since cat(1) recognizes the “-“ argument to
mean stdin, and so you can `cat a - b` in the middle of a pipeline to “wrap”
the output of the previous stage in the contents of files a and b (which could
contain e.g. a header and footer to assemble a valid SQL COPY statement from a
CSV stream.)

~~~
thaumasiotes
But that is a case where you have several _filenames_ and you want to
concatenate the _files_. The work you're using cat to do is to locate and read
the files based on the filename. If you already have the data stream(s), cat
does nothing for you; you have to choose the order you want to read them in,
but that's also true when you invoke cat.

This is the conceptual difference between

    
    
        pipeline | cat       # does nothing
    

and

    
    
        pipeline | xargs cat # leverages cat's ability to open files
    

Opening files isn't really something I think of cat as doing in its capacity
as cat. It's something all the command line utilities do equally.

~~~
derefr

        pipeline | cat    # does nothing
    

This is actually re-batching stdin into line-oriented write chunks, IIRC. If
you write a program to manually select(2) + fread(2) from stdin, then you’ll
observe slightly different behaviour between e.g.

    
    
        dd if=./file | myprogram
    

and

    
    
        dd if=./file | cat | myprogram
    

On the former, select(2) will wake your program up with dd(1)’s default obs
(output block size) worth of bytes in the stdin kernel buffer; whereas, on the
latter, select(2) will wake your program up with one line’s worth of input in
the buffer.

Also, if you have _multiple data streams_ , by using e.g. explicit file
descriptor redirection in your shell, ala

    
    
        (baz | quux) >4
    

...then cat(1) won’t even help you there. No tooling from POSIX or GNU really
supports consuming those streams, AFAIK.

But it’s pretty simple to instead target the streams into explicit fifo files,
and then concatenate _those_ with cat(1).

~~~
thaumasiotes
> Also, if you have _multiple data streams_ , ...then cat(1) won’t even help
> you there.

I've been thinking about this more from the perspective of reusing code from
cat than of using the cat binary in multiple contexts. Looking over the
thread, it seems like I'm the odd one out here.

------
mojuba
One other class of applications that don't really require garbage collection
is HTTP request handlers if run as isolated processes. They are usually very
short-lived - they can't even live longer than some maximum enforced by the
server. For example, PHP takes advantage of this and allows you not to worry
about circular references much.

~~~
chapium
This is clearly not my subject area. Why would we be spawning processes for
HTTP requests? This sounds awful for performance.

My best guess is a security guarantee.

~~~
derefr
Not spawning, forking. Web servers were simple “accept(2) then fork(2)” loops
for a long time. This is, for example, how inetd(8) works. Later, servers like
Apache were optimized to “prefork” (i.e. to maintain a set of idle processes
waiting for work, that would exit after a single request.)

Long-running worker threads came a long time later, and were indeed intensely
criticized from a security perspective at the time, given that they’d be one
use-after-free away from exposing a previous user’s password to a new user.
(FCGI/WSGI was criticized for the same reason, as compared to the “clean”
fork+exec subprocess model of CGI.)

Note that in the context of longer-running connection-oriented protocols,
servers are still built in the “accept(2) then fork(2)” model. Postgres forks
a process for each connection, for example.

One lesser-thought-about benefit of the forking model, is that it allows the
OS to “see” requests; and so to apply CPU/memory/IO quotas to them, that don’t
leak over onto undue impacts on successive requests against the same worker.
Also, the OOM killer will just kill a request, not the whole server.

~~~
mehrdadn
Thanks for that last paragraph, I'd never thought about that aspect of
processes. Learned something new today.

------
matsemann
I made a project a few years back where I had really no idea what I was doing.
[0] I had to read two live analog video feeds fed into two TV-cards, display
them properly on an Oculus Rift and then take the head tilting and send back
to the cameras mounted on a flying drone. I spent weeks just getting it to
work, so my C++ etc was a mess. The first demo I leaked like 100 MB a second
or so, but that meant that it would work for about a minute before everything
crashed. We could live with that. Just had to restart the software for each
person trying, hehe.

[0]:
[https://news.ycombinator.com/item?id=7654141](https://news.ycombinator.com/item?id=7654141)

------
FpUser
_" Since the missile will explode when it hits it's target or at the end of
it's flight, the ultimate in garbage collection is performed without
programmer intervention."_

I just can't stop laughing over this _" ultimate in garbage collection"_. What
a guy.

Btw we dealt a lot with Rational in the 90's. I might have even met him.

------
ggambetta
Of course it's also expected to crash, especially the hardware :)

~~~
Igelau
Remote execution? It was the top requested feature!

------
geophile
The problem, of course, is that the chief software engineer doesn't appear to
be have any understanding of what is causing the leaks, and whether the safety
margin is adequate. Maybe there is some obscure and untested code path in
which leaking would be much faster than anticipated.

To be sure, it is a unique environment, in which you know for a fact that your
software does not need to run beyond a certain point in time. And in a
situation like that, I think it is OK to say that we have enough of some
resource to reach that point in time. (It's sort of like admitting that
climate change is real, and will end life on earth, but then counting on The
Rapture to excuse not caring.) But that's not what's going on here. It sounds
like they weren't really sure that there would definitely be enough memory.

~~~
willvarfar
You are reading a lot into a short story. You don’t know that the engineer
hasn’t had someone exactly calculate the memory allocations.

Static or never-reclaimed allocations are common enough in embedded code.

------
b34r
I like the pragmatism. One thing that comes to mind though is stuff gets
repurposed for unintended use cases often... as long as these caveats are well
documented it’s ok but imagine if they were hidden and the missiles were used
in space or perhaps as static warheads on a long timer.

------
MaxBarraclough
On such systems the same approach can be taken for a cooling solution. If the
chip will fatally overheat in 60 seconds but the device's lifetime is only 45,
there's no need for a more elaborate cooling solution.

The always-leak approach to memory management can also be used in short-lived
application code. The D compiler once used this approach [0] (I'm not sure
whether it still does).

[0] [https://www.drdobbs.com/cpp/increasing-compiler-speed-by-
ove...](https://www.drdobbs.com/cpp/increasing-compiler-speed-by-
over-75/240158941)

------
kebman
The garbage is collected in one huge explosion. And then even more garbage is
made, so that's why we don't mind leaks...... xD

------
tjalfi
This has come up a couple times ([0][1]) before.

[0]
[https://news.ycombinator.com/item?id=14233542](https://news.ycombinator.com/item?id=14233542)

[1]
[https://news.ycombinator.com/item?id=16483731](https://news.ycombinator.com/item?id=16483731)

------
raverbashing
And that's a common mentality in hardware manufacturers as opposed to software
developers (you just need to see how many survived)

(Not saying that the manufacturer was necessarily wrong in this case and
doubling the memory might have added a tiny manufacturing cost to something
that was much more expensive)

------
wbhart
Missiles don't always hit their intended target. They can go off course,
potentially be hacked, fall into the wrong hands, be sold to mass murderers,
fail to explode, accidentally fall out of planes (even nuclear bombs have
historically done this), miss their targets, encounter countermeasures, etc.

Nobody is claiming that this was done for reasons of good software design.
It's perfectly reasonable to suspect it was done for reasons of cost or plain
negligence.

There's a reason tech workers protest involvement of their firms with the
military. It's because all too often arms are not used as a deterrent or as a
means of absolute last resort, but because they are used due to faulty
intelligence, public or political pressure, as a means of aggression, without
regard to collateral damage or otherwise in a careless way.

The whole point here is the blase way the technician responded, "of course it
leaks". The justification given is not that it was necessary for the design,
but that it doesn't matter because it's going to explode at the end of its
journey!

~~~
willvarfar
A simple bump allocator with no reclaim is fairly common in embedded code.

Garbage collection makes the performance of the code much less deterministic.

A lot of embedded loops running on embedded in-order cpus without an operating
system use cycle count as a timing mechanism etc.

~~~
wbhart
Right, but that isn't the argument that was being used here, which is my
point. The way I read it, the contractor cared only enough to get the design
over the line so the customer would sign off on it. Their argument was that
you shouldn't care about leaks due to scheduled deconstruction, not because of
a technical consideration.

There exist options between no reclaim and using a garbage collector which
could be considered, depending on the exact technical specifications of the
hardware it was running on and the era in which it happened.

But retrofitting technical reasoning about why this may have been done is
superfluous. The contractor already said why they did it, and the subtext of
the original post is that it was flippant and hilarious.

~~~
ncmncm
Fetishism is not compatible with sound engineering.

"Cared only enough" is just your projection. The contractor knew the
requirements, and satified the requirements with no waste of engineering time,
and no risk of memory reclamation interfering with correct operation. The
person complaining about leaks wasted both his time and the contractor's.

~~~
Dylan16807
You had a good comment going until the last sentence.

When your job is performing an analysis of the code, five minutes asking for a
dangerous feature to be justified is ridiculously far from a "waste of time".

------
djsumdog
My undergraduate mentor took a co-op position one year in Huntsville, Alabama.
He told me about 6 processor missile guidance systems that cost tens of
thousands of dollars ... all to guide a missile to where it gets blown up.

------
kleiba
Seems a bit unlikely to me. Intuitively, calculating how much memory a program
will leak in the worst case should be at least as much effort as fixing the
memory leaks. And if you actually _calculated_ (as in, proved) the amount of
leaked memory rather than just by empirically measuring it, there's no need to
install double the amount of physical memory.

This whole procedure appears to be a bit unbelievable. And we're not even
talking about code/system maintainability.

~~~
FreeFull
A memory allocator without the ability to free memory is a lot simpler and
faster. Usually though, I'd expect to see static allocation for this sort of
code, I'm not sure why a missile would have to allocate more memory on the
fly.

~~~
StupidOne
Because he needed more memory mid-air? :)

Not sure was it pun or no pun intended, but you gave me a good laugh.

------
32gbsd
It is all good until people start to depend on these memory leaks and then you
are stuck with a platform that is unsupported.

------
lallysingh
Are these Patriots? Didn't they need a power cycle every 24 hours? Is this
why?

------
simonebrunozzi
> the ultimate in garbage collection is performed without programmer
> intervention.

Brilliant.

------
MrBuddyCasino
Why go through the trouble of

a) calculating maximum leakage

b) doubling physical memory

instead of just fixing the leaks? Was it to save cycles? Prevent memory
fragmentation? I feel this story misses the details that would make it more
than just a cute anecdote.

~~~
kelvin0
I feel the same way too, dunno why the down votes? In the absence of all other
details it just seems like shoddy work, but of course reality is probably more
nuanced ... which is what's missing from the story.

~~~
ratboy666
I think the story isn't nuanced. The program runs once, the missile explodes,
garbage collection is done!

No need for garbage collection, no need for "memory management". Not shoddy
work. An expression of "YAGNI". The interesting thing (in my opinion), is the
realization. The teller of the story went to the trouble of discovering that
memory is leaking. She could have simply asked before engaging the work.

FredW

------
DagAgren
What a cute story about writing software to kill people by shredding them with
shrapnel.

~~~
daenz
Missiles are also used for defense to intercept threats.

~~~
ptx
Those threats are sometimes an attempt at retaliation by whoever was attacked
earlier by those now defending against the counter-attack, who are now free to
attack without fear of the consequences thanks to the missile defense system.

------
zozbot234
(1995) based on the Date: and (plausibly) References: headers in the OP.

