This is the second time this week on HN that I've seen people suggesting object pools to solve memory pressure problems.
I generally think it's because people aren't experienced with diagnosing and fixing memory pressure. It's one of the things I do pretty frequently for my day job. I'm fortunate enough to be the "performance" guy at work :).
It'll always depend on what the real issue is, but generally speaking the problem to solve isn't reinventing garbage collection, but rather to eliminate the reason for the allocation.
For example, a pretty common issue I've seen is copying a collection to do transformations. Switching to streams, combining transformation operations, or in an extreme case, I've found passing around a consumer object was the way to avoid a string of collection allocations.
Even the case where small allocations end up killing performance, for example like the autoboxing example of the OP, often the solution is to either make something mutable that isn't, or to switch to primitives (Valhalla can't come soon enough).
Heck, sometimes even an object cache is the right solution. I've had good success reducing the size of objects on the heap by creating things like `Map<String, String>` and then doing a `map.computeIfAbsent(str, Function.identity());` (Yes, I know about string interning, no I don't want these added to the global intern cache).
Regardless, the first step is profiling (JFRs and heap dumps) to see where memory is spent and what is dominating the allocation rate. That's a first step that people often skip and jump straight to fixing what they think is broken.
You might want to take a look at what actually happens here. The only reason Go can compile this fast is that it does barely any optimizations. And yeah, javac also does a literal transfomation to Java byte code, no difference here with Go. One will just get a second chance of actually getting optimized via a JIT compiler.
And Java AOT is not doing the same as Go, so again, apples to oranges. It does a complete closed universe build of the program, which requires knowing what classes are ever reachable, and then running their initializers at build time, so they can be burnt into a binary in an initialized state.
There used to be a GNU java compiler doing pretty much what Go did, it's not hard to build, it just wouldn't be compatible with "normal Java", so the use cases would be limited.
The e2ee you can “enable” is a homemade custom cipher and cryptosystem. There are no open source implementations.
I have no evidence that Telegram is an FSB honeypot. What I do know is that if the FSB had a honeypot chat platform, it would look and function exactly the way Telegram does.
Honestly can't tell if this is not sarcasm/rage bait.
Teams that has 3 different UI frameworks on every platform (but your best bet is the web)? With the Microsoft login that tends to loop forever redirecting to God knows where?
It's just that Maven doesn't have a good core abstraction and it is not a reliable build system. Like even with base plugins, let alone with additional ones you can't be sure that a build actually picked up every change, you often have to do a double take and do a clean install instead to get some stale files cleared. This should never happen in a build tool and every other feature is secondary to this error.
That's why I defaulted to Gradle, which has its own idiocities (like tending to break the syntax on every second major version, but it's much better with the kotlin DSL), which at least 100% sound.
For more experimental/hobby projects I choose mill though.
But it's IDE who picks up every change, not Maven. God forbid you run 'mvn install' for all modules on every line change while developing, that's IDEs job. Maven config just tells IDE locations and dependencies.
For release build you do want to clean up the space in CI/CD anyway.
Not sure what you mean by "doesn't have a good core abstraction". For example, Linux famously doesn't have a good core abstraction (aka "monolithic kernel").
It's not a good core abstraction for a build tool to not be able to do proper iterative builds. And thus it's not a good model for IDEs neither, while with Gradle it never gets out of sync, with Maven it can easily happen that some edits don't "show up" in behavior.
Unless you are using a strictly FP language, you are always basically using objects.
And all the stuff like factories and everything exist in FP as well, it's pretty short sighted to say otherwise. These are patterns that may or may not be useful in a given case. Sometimes a language feature can replace them (e.g. pattern matching ~ visitor pattern, but not even this is a full match see closed vs open hierarchy), but in most cases you just call them something else/don't have that problem because you are not writing that big of a software.
I do mostly use a strictly FP language, but it's trivial to use Java (C#, etc) as FP languages too. Just use classes as stateless function namespaces. FP Java is best Java.
> And all the stuff like factories and everything exist in FP as well, it's pretty short sighted to say otherwise. These are patterns that may or may not be useful in a given case. Sometimes a language feature can replace them (e.g. pattern matching ~ visitor pattern, but not even this is a full match see closed vs open hierarchy), but in most cases you just call them something else/don't have that problem because you are not writing that big of a software.
This is precisely the viewpoint that made me want to reply to the original comment. If you live and breathe OOP, you see OOP design patterns as solutions, because you've run into OOP problems that they solve. FP code does not tend to run into OOP problems, and ergo does not tend to utilise OOP solutions to those problems. I appreciate this can be hard to imagine, but I respectfully suggest you have some OOP blinders on if you're so confident everyone is using factories that you attempt to size-shame FP codebases you've not even seen so you can remain in the right. :)
The purpose of factories is to decouple object creation and use. But FP already does this by never coupling behaviour and data in the first place. Think about it. If data is in a simple struct, with no member functions, ideally immutable, what kind of factory could you possibly need? How would it even work? What kind of problem could it possibly solve?
To say nothing of e.g. the Visitor pattern, which is an approximation of functional styles using OOP. There is no need to define some small subset of behaviour outside a class, and then hook up boilerplate machinery inside that class to run that behaviour (ie the Visitor pattern) if all behaviour is always defined outside the class. The Visitor pattern solves a problem that arises only in OOP, due to its penchant for stateful data-code coupling, and it does so with a sprinkling of FP. It's incoherent to speak of a Visitor pattern in FP code. It's an OOP solution to an OOP problem.
I am fairly familiar with FP concepts, though lack experience with large FP-only code bases, so I don't think the OOP-blinder things apply to me.
Visitor pattern is meant to address the other half of the expression problem - you have n types of data all with m behaviors. OOP makes it easy to add a new row (new class) while FP makes it easy to add a new column (new function). But both are asymmetrical, traits a la rust are probably the closest to solving it properly. So no, it is incorrect assumption on your part to hand wave it away, this is not solved (especially not in "FP java", which otherwise I also like and use).
With all due respect, I'm fairly sure that anyone using "VM" the same way as you do here really think of it as a container or what.
It's a runtime, and go also has a similar, fairly fat runtime. It's just burnt into the binary instead of being shipped separately. (Hell, even Rust has a runtime, it's just very very lean compared to languages featuring a full GC like go and java)
I wouldn't use application servers today, but they were solving many of the same issues as whole kubernetes clusters do, decades ago.
The Java (now Jakarta) EE standard on top is a good base for third party implementations to "speak a common language", e.g. it's not that hard to move between Quarkus/spring/micronaut etc, even if not all support the actual standard.
It's replete with oddities and limitations that signal "ah, this is because systems language."
Go’s type system, for example, is very much a systems-language artifact. The designers chose structural typing because it was lighter weight, but provided enough type safety to get by. It sucks though for enterprise app development where your team (and your tooling) are desperate for nominal typing clarity and determinism.
The error handling is like a systems language for sure, I'll agree on that.
But where do Go's docs or founders call it a C replacement? gf000 asked where this is mentioned besides marketing, but I don't see it in the marketing either.
Thanks. I'm not surprised they called it a C++ competitor back then. All those systems-style features do make it awkward now that it's targeting the Java-like use cases. No pointer arithmetic, but pointers yeah, and it's not very clear what you're supposed to pass by value vs by ref. You can do things like a DBMS in Go that count as systems programming, but for sure it's not a competitor with C.
Go happened to be attractive for web backends too because it had good greenthreading before Java etc did, and better performance than JS, so it makes sense that they changed the marketing.
Go's runtime is thin: goroutines, a GC specialized for concurrency, networking, and little else. Java, by contrast, assumes a JVM plus massive stdlibs to handle everything from enterprise apps to big-data, making its platform genuinely "fat" and layered. Other Java-tier languages, C# included, follow the same model.
I agree Go's runtime is as thin as runtimes get. But having a runtime at all disqualifies it from being a C replacement. Rust is trying to replace C, Go is trying to replace Java and some of C++.
Mill does work with idea (via the build server protocol), and the ideas behind it are very sound (a build tool is basically a function calling other functions - on which they depend. You just want to parallelize their running and cache their results.
But it does have a learning curve and you may sometimes end up having strange error messages. (As an implementation, it's basically Scala macros turning normal looking scala functions into a static task graph). It is getting better and better support for mainstream java build setups, and it's possibly the best tool for something very custom. In between the two extremes, you may or may not have a better time with Gradle/Maven.
The JVM may optimize many short lived objects better than a pool of objects with less reasonably lifetimes.
reply