Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I led our teams to switch from Java to Go because of the productivity of development, but then noticed deployment was simpler and faster, memory usage was slashed (for comparable applications), request/response times were much more consistent, startup was practically instant and as a result we started aggressively rewriting Java applications to Go and saw a notable difference in the number of machines we needed to run in AWS.

So in my situation, the JVM is heavier by every single measure listed, and for each by a considerable margin.



> aggressively rewriting Java applications to Go and saw a notable difference in the number of machines we needed to run in AWS.

This is the easy trap to fall into though. What if you aggressively rewrote the Java apps from crappy legacy frameworks to well developed Java apps?

A rewrite ALMOST always is faster. So the new language seems faster. Except if you would then rewrite the rewrite back in the original language... you could even still be faster.

Very hard to split apart what is faster because the rewrite got rid of lots of bloat, and what is faster because it is legit faster. Java is legit fast when it is written well. Also very easy to make a bloat fest.


These apps are mostly microservices and the Java ones are mostly only a year or two old. None of them use things like spring. Some use Dropwizard. Would you consider dropwizard modern? If not, what would you use instead?


Take a look at the TechEmpower benchmarks:

https://www.techempower.com/benchmarks/

DropWizard is modern, but it isn't fast. Go and even Node.js are significantly faster. If you want performance, you cut layers out of the stack - check out the numbers for raw servlets or even just straight Jersey annotations in that benchmark. If I were doing JSON-over-HTTP microservices in Java, I'd likely use straight Jersey + Jackson, or if performance was really a problem, Boon over straight servlets.

What framework did your Go rewrite use? The standard libs?


Boon is not that fast. It only appears fast on some poorly constructed benchmarks due to some lazy benchmarketing optimizations.

https://github.com/fabienrenaud/java-json-benchmark


On first glance the dropwizard test app appears to be doomed to mediocrity via reliance on hibernate.

Call me crazy, but I like my dropwizard with Spring DI for (singleton) resource setup, a micro-ORM to get work done, and HikariCP datasources at runtime.


What's wrong with hibernate? The only thing I can think of is that you're not using "JOIN FETCH entity.relation" when accessing collections and end up with the N+1 select problem but that is because you're using any ORM incorrectly.

Entity framework has include and active record has includes which do the same thing. The qt ORM also has something similar.

The only ORM I have seen that lacks this critical feature is odb. It doesn't allow setting the fetching strategy on a per query basis. You have to either always use eager loading or lazy loading which basically makes it useless for my purposes.


Well, for benchmarking the essential framework, which does not mandate any ORM, I would want to use something for data access that takes the question of time spent on type reflection, internal caching, and the like, out of the picture. Hibernate and EMF have their place, but not as part of benchmarking the thing that hosts 'em. Core Dropwizard performance is all about how it uses Jetty, Jackson, and maps requests to resources and operations.


> DropWizard is modern, but it isn't fast. Go and even Node.js are significantly faster.

Any benchmarks to provide in order to support this wild claim?


The ones I just linked to above.


Use vertx if you want lean REST micro-services. I so wish vertx was part of the standard library.

The main advantages that Go has over Java is that the standard library is brilliant - thus obviating the need for folks to create monstrous frameworks (and losing performance) and that Go has better memory utilization because of value types (structs) and because it is AOT compiled. Unfortunately Java JIT as designed by the JVM devs takes a lot of memory.

In raw performance, I would still give the edge to Java over Golang though.


A lot more to an app than MCV framework. I realize dropwizard tries to be the everything for the app, but at the core it is a MVC with some bundled libs.


> This is the easy trap to fall into though.

Indeed.

It's a typical honeymoon phase with very little regards to 1-2-5 years in the future. The cost of having picked to Go will be fully apparent then.


I really want to see a comparison of a language like go to something much more in the functional sphere when it comes to maintainability of a large codebase.

I really feel like that one of the big issues we as programmers want to get a better handle on but there isn't a lot to go off that isn't based off opinions (which can be hard to validate).


Yes, the limitation is rarely the programming language, it is the programmer.

Also, when you do the rewrite you have already solved the domain problem that you did not fully understand when implementing it the first time.


"Plan to throw one away; you will, anyhow." First version to understand the problem, second version to solve it.

But deployment, gc pauses and startup time (jvm vs go) are orthogonal to program quality. I would also expect go to have less memory usage.

> deployment was simpler and faster, memory usage was slashed..., request/response times were much more consistent, startup was practically instant


Orthogonal to quality but imperative to velocity.

At the end of the day despite Go's failings it's a good (maybe the best?) language for large projects and teams because it compiles fast, is easy to anyone to run anywhere, tests run quickly, programs execute quickly and there is already good tooling/editor support.

Nothing beats efficient workflow for improving velocity.


I could use the exact same arguments but for PHP.


Not quite, I omited here that it is also statically typed and is a future proof language. Mainly because these are properties already shared with Java. However this is not true of PHP.

PHP is a great velocity language, provided you have a small(er) team or are willing to commit to additional controls on how you write your PHP (document types/structure of arguments mainly) to ensure that your PHP code is able to be read quickly by other developers.

Personally I prefer Go here because it enforces good readability by default and therefore scales better with team size.


Readability is not problem in PHP either. Follow PSR-1, PSR-2 and PSR-4 and use a command like tool like codesniffer in your build step to guarantee code standard on each commit (or use Upsource)

And in PHP7.x you have even more type hinting than before and with an IDE like PHPStorm refactoring is a breeze.

And with the release of PHP7, PHP is future proof. The community will continue improve it with the major features, they have shown it. Interest in the language have increased. More RFCs is contributed to the language than before. https://wiki.php.net/rfc

Multiple teams on a large code base is not really a problem in modern PHP. I do it every day. We follow modern design patterns, code reviews, code coverage over 80% of the system (old as new code). New code is probably over 95% coverage. Deploys regularly multiple times every week.

Almost all (>95%) of my problems stem from design decisions made in the past, not the language itself.

I'm not saying that you should not use Go (or Java). Both are fine languages. Use the right tool for the job. If you don't do a realtime stock trading system or some embedded system, but some web stack, I can't really see that the majority of the problems stem from language choice (whatever you choose). It is in the team, the culture, the understanding of the domain. There should be your focus.

Personally, the most two important things I look for in a language/platform is tooling and community.


Which is why I still often use PHP.

For my usages its a reasonable language.


You're still limited by the JVM technology, regardless of how you write your app - large heap, and big tail latencies (JVM's GC is designed to be throughput optimised, whereas golang is latency optimised).


Just use one of the other JVM's that have GC that are latency optimised such as Zing from Azul, Metronome from IBM or OpenJDK with Shendoah from RedHat.

The power of Java is that there is more than one JVM and that can really save you a lot of money/developer time if the world changes under your ass ;) i.e. had a JVM based graph database, ran it on Hotspot -> big GC pauses, moved to Zing no more pauses. All we needed to do is run a different VM and problem went away (new problem was of course that Zing costs but not much, also now with Shendoah coming for free we could probably have moved to that)

With GO you can't do that yet. If your app is not latency, bound but throughput bound there is no place to switch too other that a rewrite. That flexibility of deployment on JVM tech gives us a lot insurance for no costs, until we need it.


Actually, the new G1 collector deals very well with latency-sensitive workflows. I'd say it's comparable to Go if you adjust your heap size to the working set. You can try running the benchmarks here - https://gitlab.com/gasche/gc-latency-experiment.


Exactly, that's how some not very bright people were tricked into thinking that Node.js is actually fast.


And at work, we're now rewriting all those NodeJS services in Go or Java.

We hired some Node maintainer(s) a long time ago, rumor has it, who got us on the Node train.


How do you do async in java? While it does have CompletableFutures now, none of the libraries (specifically databard drivers) seem to support it, so I always end up with a blocked thread per request.


Java has had non-blocking IO for some time. https://en.wikipedia.org/wiki/Non-blocking_I/O_(Java)

Unfortunately it seems difficult to use (to me at least), but frameworks like netty are build on top of it to provide incredible performance.

However, the fact that Java provides real threading means that a blocking io is not a performance problem if you use the correct patterns.


I've spent a fair bit of time in both, most recently the last couple of years in go. I think its a very mixed bag and there is no clear winner.

The tooling, especially for runtime operations, are so much superior to the golang options its night and day. I have much more success modeling complex business models in java with its better type system, and for doing low latency work its much easier to do on the jvm due to the availability of better libraries (which may get better in go) and the concurrency options are miles better on the jvm.

Go's stack allocation and gc defaults make for easy management in most of my default cases. The ease of adding http endpoints to things is phenomenal. Being able to write easy cli applications in the same language I write daemons in is great.

All told, I think for simple daemons and cli's I'd go golang, for more complex systems I'd go jvm.

I, personally, think the binary deployment thing is overblown. I've never had any problems deploying jvm applications and the automation to do either seems essentially the same to me.

As for the relative "heaviness" I think golang definitely feels lighter, but that is largely because golang apps do less. Once you start having them do more they start to "feel" just as heavy as java apps (for whatever "feel" means).

* [edit] called golang heavier meant lighter


I have golang website/web app that runs at tens of megabytes per process. A very similar java web app runs in a few hundred megabytes per process.

I also run these in on cloud platforms that auto scale. The golang processes spin up very quickly, the java ones not so much.

In these two respects the JVM is heavy compared to golang for my very common scenarios. The heaviness also causes me to spend more money for the JVM solution.


But what max heap size did you set for the JVM?

I have an app that people were complaining took too much memory. A quick look with VisualVM showed that its actual heap usage when idling was only 50 mb but because we hadn't set any heap size limit, it was reserving hundreds of megs from the OS. The idea is that it can run faster if it does that. The fix was simply to use the -Xmx option to tell it to use less memory and GC more often.


The JVM is very inflexible in that respect. If you give it more memory it will keep all of it way beyond the point where it matters for performance. If you give it less memory you need to know exactly how much less you can give it before performance craters.

In other words, JVM deployments need a lot more tuning than Go and they will generally need a lot more memory as well. But you're right, not setting -Xmx at all will make the JVM look worse than it really is.


We have similar experiences as well.

  $ ps -eo rss,cmd,user | grep jenkins
  4928228 /usr/bin/java -Djava.awt.he jenkins

  $ ps -eo rss,cmd,user | grep drone
  12940 /drone agent                root
  19924 /drone server               root
We run the two applications in the same machine. Admittedly Jenkins is much feature-rich but we only use its vanilla settings without whatever fancy plugins for a few legacy SVN repos.

P.S. The Drone server and agent are running within docker containers.


I don't know why you are getting downvoted for sharing your real world experience, lately "fanboyism" on HN is getting out of hand. I have similar experience with one of the services that was ported from Java to Go.


"Don't rewrite an application from scratch. There is absolutely no reason to believe that you are going to do a better job than you did the first time." -- Joel on Software, Things You Should Never Do, Part I [1]

[1] https://www.joelonsoftware.com/2000/04/06/things-you-should-...


I do like that article but have ignored it several times for good reasons.


I think a better interpretation of the title/article is "Don't assume that rewriting from scratch will fix all of your problems"


These types of arguments cause many intelligent people to headdesk. They're hardly an apples to apples comparison.

Of course "Go was Faster". It's because you started with a clean slate!


That's not it. A fairly small http server in go will run in tens of megabytes. The same thing on the JVM requires a couple hundred megabytes at best. The difference in startup time is roughly the same as well.


Are you sure? I've written small HTTP servers in Java that can happily run with a heap of 30-50mb or less. Runtime overheads add some on top of that, but not much.

I think the perception of Java suffers a lot because it will consume all the RAM on your machine by default if you let it (but not immediately). It's a very poor default because even though there are technical arguments for doing that (goes faster), they aren't well known and people tend to assume "more memory usage == worse design".

There are a lot of myths about the JVM out there. We can see on this thread the idea that it takes 1.5 seconds to start being repeated multiple times, each time someone else points out that it's actually more like tens of milliseconds to start.


> I've written small HTTP servers in Java that can happily run with a heap of 30-50mb or less. Runtime overheads add some on top of that, but not much.

I second that. I have deployed a medium traffic web-server written in Scala backed by a postgresql DB on 128MB VPS, back in 2009!

> I think the perception of Java suffers a lot because it will consume all the RAM on your machine by default if you let it (but not immediately).

I don't think that is true. The default heap size for Oracle and OpenJDK VMs has been bounded as far as I remember. In fact, I would like it if the VM, by default, allowed the heap size to grow upto available RAM when GC pressure increases, but that doesn't seem to be the case as of now.

Edit: Did you mean non-heap VM arenas grow indefinitely? If so, I am not aware of them.


Must be Java 6 in 2009. Java memory usage increased with new releases to make it perform better. For medium traffic site it would have worked fine because GC would have ample time to clean unused objects.


128mb is a lot compared to go which will often run around 10mb. Was your jvm back then 32mb or 64mb? If it was 32 your memory requirement will be higher on 64.


128MB was the total RAM in the VPS including OS + nginx + JVM + Postgresql. The heap allocated to the JVM process was about 64MB, but bear in mind that this was an actual application. So, it's hard to do a detailed comparison between JVM and Go without standardising on the application. All that I am claiming is that JVM is in the same ball park.


It's not in the same ballpark. I'll throw some code up when I get a chance.

Edit: do you have a twitter or Reddit account? I'll ping you when I have code examples if you want.


> it will consume all the RAM on your machine by default if you let it

I wonder if Oracle documents are plain wrong for JDK 8 docs for maximum heap size[1]:

"Smaller of 1/4th of the physical memory or 1GB. Before Java SE 5.0, the default maximum heap size was 64MB. You can override this default using the -Xmx command-line option.

Also Oracle has chosen correct defaults because it took Java long time to shed its reputation of being dog slow and if they optimize for memory it will start looking worse in performance.

1. https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gc...


Try to do it and shoot me a github link. Trust me, I've tried, but I'm no JVM expert so there could be some magic flag I'm unaware of.

I can get it to start around 30-50mb, but as soon as you hit it with traffic the memory usage jumps up.


It was a couple of seconds on my Cyrix 300MHz-ish CPU back in 1998. I would have expected it to get a little better since then.


I have a little server I wrote in Java. Admittedly it is not a HTTP server, but it quite happily handles a thousand simultaneous connections with a memory limit of 200MB. It's currently sitting around 26MB, but I'm sure some of that would disappear if the VM did a GC.


Not correct at all. You can run Tomcat in 5mb of ram and it starts in less than 250ms


As another commenter mentioned, I think this is much more the programmer and less the language. Sure, the language may recommend certain approaches which carry across teams differently, but it still often comes down to the app, not the language. I implemented a rudimentary Java AOT targetting Go and the trimmed-down stdlib grew so big Go took hours compiling it (granted some of that is how I approached OOP and what not).


> I implemented a rudimentary Java AOT targetting Go and the trimmed-down stdlib grew so big Go took hours compiling it

Have you reported to Go devs? Sounds interested use case.


Yes[0] though I think the issue title is a bit off. They did bring it down from over 7 hours to 30 minutes ish in a recent release, but it's still is too long, too much CPU, and too much mem. They are very reactive of course which is something I can never say about OpenJDK.

0 - https://github.com/golang/go/issues/18602


> memory usage was slashed

A lot of this has to do with another unmentioned, terrifically annoying property of the JVM: pre-launch min/max heap allocation. Standard operating procedure is to go with the default, and overbump it if your needs exceed it. I can't possibly imagine how many petabytes of memory are unnecessarily assigned to JVMs throughout the world as I type, apps consuming 79MB with a 256MB/512MB heap size.


I wonder how much of this is due to a couple differences: in Go you can embed structures in others instead of using a pointer, strings are UTF-8, and arrays (slices) are resizeable by default.

(I'm sure a chunk of the difference is due to a better understanding of the program during rewrites.)


These sorts of comments are (no offense) worse than useless. Benchmarking is one of the most difficult things to do in software, and anecdotes like this just make things confusing for new engineers and feed the perpetual hype train around newer languages.

Please refrain from making statements like this unless you have a reproducible quantifiable analysis.

If you really wanted to demonstrate the effect you describe you'd need to have the same team rewrite the application twice, once Java->Java, once Java->go, making sure to align the program structure as much as possible (making exceptions to take advantage of lang specific features of course).

If you were to do that, then that would be interesting! No one does that of course because it's expensive and wasteful from a business perspective, but it's the only way to determine anything useful.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: