Do Not Use Go for 32bit Development
abtinforouzandeh.com
abtinforouzandeh.com
The fix for both this issue and the pinning issue is big changes to the garbage collector. Since the language and standard library have stabilised with Go1, changes to the runtime are going to be the focus of development so this issue will probably get a fix.
If you want to see exactly what's happening, SysInternals VMMap (http://technet.microsoft.com/en-us/sysinternals/dd535533?ppu...) will tell you exactly the VA space layout.
Note that Linux has exactly the same problems with VA space, except they default to 3/1, instead of NT's default (but configurable) of 2/2.
It seems that a quick fix to this issue would be to lower that number from 512MB to something like 128MB, though I know nothing about Go's internals.
Isn't this what enables the operating system to scramble the allocations it gives you to make it harder to implement a buffer-overflow attack?
Right. Userspace uses virtual addresses, which get mapped through page tables to become physical addresses. Some kernel addresses get identity-mapped (meaning that the virtual address matches the physical address), while some kernel addresses go through translation as well. (Specifically, on Linux, kmalloc allocates identity-mapped addresses, while vmalloc allocates virtual addresses; vmalloc allows you to have a large virtually contiguous buffer without requiring a large physically contiguous region of free memory.)
> Isn't this what enables the operating system to scramble the allocations it gives you to make it harder to implement a buffer-overflow attack?
You can do Address Space Layout Randomization (ASLR) for either virtual or physical address spaces; you want to do it for whatever type of address an attacker could otherwise make use of. Userspace processes can use ASLR for their virtual address space, so that attackers can't make use of fixed addresses. The Linux kernel doesn't normally map virtual pages to any particular well-known location in physical address space, either. The kernel can also use ASLR for some (though not all) of its own kernel-space addresses. Beyond that, recent versions of Linux also try to avoid exposing kernel-space addresses to non-root users.
Also note that ASLR doesn't generally introduce enough randomness in a 32-bit address space.
You can change this in boot options to 3 or 4 (optimized) or enable PAE. see:
http://msdn.microsoft.com/en-us/library/aa366778.aspx
i'd try that before rewriting your code (also kill services you don't need, which is usually a lot of them. run -> msconfig).
Edit: you also don't mention probably the most important piece of info: which version of windows you are running.
Edit 2: make sure go is being treated as a background process
Edit 3: it seems a lot of these tips should be in the Go docs for win32 - i'll add it when I have a moment
I was wrong, it does, it's on by default :).
Can you clarify?
For one we could not get correctly our game and some of our tools to work on XP with /3GB (that was few years ago)
Nowadays since we moved to Windows Vista/7 64-bit, we no longer have the problems (LARGEADDRESSAWARE) and our 32-bits sometimes go up to 3.5gb of usage.
I get the sense from various aspects of this guy's story that he's shipping executables to end users, which makes this "solution" a non-starter.
You can't expect the user to go in and mess with these sorts of options just to run your program, and I wouldn't feel comfortable changing something like this automatically during install since it is such a fundamental OS setting (not to mention testing the installer against all versions of Windows would then become a nightmare you'd have to redo every release).
While it is true that most garbage collected environments will reserve some memory at startup, a hard minimum of 512 MB is not the norm. Do note that embedded Java has run on cell phones with minimal amounts of RAM for many years.
It is NOT a Go problem, but Go could implement a workaround making the address space reservation in teh PE header.
Why was kernel32.dll rebased when Go doesn't force the rebase is a very good question indeed. I suspect a 3rd party user mode hook, it doesn't even have to be malware, there are legit "security" and monitoring applications that use this technique.
It's definitely something to investigate though.
Understanding the why of the issue is extremely important in this case. Based on the discussion in the bug report, I would expect that using the 32 bit compiler for 32 bit architectures and 64 bit for 64 bit architectures would not result in the issue occurring. Before abandoning Go, I would very much identify that this is indeed the case. With Go's young age, I would very much use the architecture specific compilers for the specific targets which is different than normal expectations, but not at all unreasonable.
Edit: similarly, 64bit also is immune because of how the main platforms allocate virtual address space to clients.
It could be solved by making the initial virtual address space reservation in the PE header, not requesting it in the initialization path, that would always work, even with cgo.
char mem[512 * 1024 * 1024];
the executable loader would "allocate" this memory before loading any dlls, and later dlls that were supposed to be in that memory would be rebased
Oh, and somehow Go would need to use this memory rather than allocate it.
For example, if the language runtime was loaded as plugin or something. So one has to be aware that is not always the good solution for all purposes, hence hacky :)
(Yes, it may be lazily committed, but it's still not as cheap as address space.)
I don't believe a single word. He pretends to have written "a ton of Go code" without discovering the "real show stopper"? I'm not a Go fan (quite the contrary, Go isn't the step forward from C++ and Java I expected from Google). But this Anti-Go campaign few days after 1.0 smells.
Go is a lovely language, imo. Quirky in parts, but definitely a charmer, very fun to code in, and once you get the Go zen, simple and elegant code effortlessly follows. I haven't had this much fun since Java came out in mid 90s.
Of course, I do agree with the other comments that would like to see Golang.org be more clear as to the platform coverage and the current state of Scheduler and MM -- but they are certainly upfront in the source tree (c.f. proc.c header comment), forums, and the issue tracker.
So hacker fine print is there and available, and frankly (IMHO) that approach to disclosure may turn out to be a good filter for the growth of Go community.
The issue at hand is nothing that time and resources will not solve -- they just ran out of time (c.f. RSC's comment before Abtin's in the issue.)
>
>Go is a lovely language, imo. Quirky in parts, but definitely a charmer, very fun to code in, and once you get the Go zen, simple and elegant code effortlessly follows. I haven't had this much fun since Java came out in mid 90s.
Go is a step backwards in language abstractions, throwing away many of the abstractions that have become mainstream in the last decades.
Just because some abstractions have become mainstream doesn't mean they are good.
I think precisely what many people love about Go is that it throws away lots of unnecessary abstractions and complexity that people have come to expect from languages this days.
Go's approach is a breath of fresh air.
Creating bad PR by releasing "stable" code containing undocumented landmines sure is likely to affect the growth of the community.
Please consider this:
a version 1.0 of an open source project X has been released by a team that actively engages its user community. Release manager indicated before release (in issue tracker) that a special problem with platform P is not making the cut. It is true he didn't tweet it /g but it was a public announcement.
Developer D has decided to use X1.0 to create a software product for paying customer C on said platform P on a tight production go live deadline. Apparently there were issues in his deployment testing regiment, as his initial tests were apparently satisfactory, and he typed a ton of code, and now he is busy retyping the same in C as something went boom with X 1.0 on platform P.
IMO, it is not thoughtful to spec 1.0 software for your paying clients unless you know what you are doing and have land mine detectors and have reviewed field maps. (An example of folks fitting the requirement are Heroku -- they use Go in production.)
IMO, stories like this will (a) stem the rush of those who compulsively (but superficially) seek 'the new' and then blog about their own misunderstandings; and likely keep thoughtful hackers who like to dig deep (and will avail themselves of the public resources and available channels) and make their own determination. And these hackers will build great software on Go and that will attract more developers.
There is no real reason for Go to seek a footprint via "PR". PR for languages is for those who need users for their language's survial. Sun (RIP) didn't really need Java, did it? They had to push Java to gain users. Go has Google for its deployment foot print (as a client of sorts); is already an option in the cloud.
Go has a solid team of engineers [1] behind it. It is definitely not a flavor of the month language and would not benefit from a flavor of the month community.
All opinions, of course!
[1]: for a sample of both: http://www.youtube.com/watch?v=HxaD_trXwRE
Until you actually have to deploy the code on end-user 32-bit systems, it is very easy to be blissfully unaware of this issue. And since Go1 "supports" Windows-x86, why would you assume anything other than that your code will "Just Work" on such a target?
I don't think it is unreasonable to expect the Go team to give this issue more light. In my case it doesn't really matter, but if I were using Go to write end-user apps where 32-bit Windows installs were on the table, I could understand this guy's frustration pretty easily.
The technical details are interesting (https://groups.google.com/forum/#!msg/golang-dev/EpUlHQXWykg...) and Linus' response (http://lkml.indiana.edu/hypermail/linux/kernel/1102.1/00233....) is also instructive.
Unlike other 32-bit x86 operating systems, the consumer version of Windows refuses to addresses more than the first 4GB of physical memory, and on most machines a quarter to half of that space is consumed by PCI address space.
Yes there are hacks around this. But the right way to deal with this is to just fold and accept what Microsoft is imposing: use a 64-bit version of Windows whenever possible and enjoy your memory.
Once on a 64-bit OS, you can either write a 64-bit program, or you can write a 32-bit program and use a compiler option to get the full 32-bit address space. (Microsoft's compilers assume by default that you are an idiot who uses signed values for addresses and your program will break if they give you more than 2GB of virtual address space)
The other lesson is don't use experimental languages you are uncertain of for actual product.
I do agree that Go on 32 bit Linux rarely has any problems, but this is true for Windows as well. What happened in the last few days is blown out of proportion.
Note: I'm not a Go dev, or have even programmed in it, but logically it should be a quick fix that you can try to save you rewriting your codebase.
Given the importance of the number it's probably a #define or constant so should be pretty easy to find in one of the header files.
We're considering using Go for an end-user desktop application for Mac, Windows, and Linux. Of course, we don't have control over whether our users have a 32-bit or 64-bit system. And, of course, our app has to work 100% of the time. What can we do to make Go work 100% of the time on these 32-bit systems?
The fact that this is not happening means that 3rd party DLLs are loaded, this happens if you use a program, knowingly or not, that install user mode hooks.
Probably no OS supports this any more. But the instructions set and memory management units support it, or used to in early Pentium days.
That's true, but segment descriptors have a not-present bit, which allows you to implement segment-level swapping.
Also, as noted elsewhere, on 32-bit architectures there's segment swapping too. In fact I wrote an OS that segment swapped before the 386 came out, when the 286 was king. Probably the only one out there; a pretty crazy notion and the 386 came out a year later with paging.
The point about being able to implement segment-swapping is well-taken however.
Is there someone here who can say either: (1) Go will fail sometimes on 32-bit systems, don't use it until this problem has been fixed, or (2) there are things you can do to always avoid the 32-bit problems.
?
Edit: Okay I see a memory map on the bug report. So why not poke at a copy of kernelbase.dll?
From the project goal page:
"Go is miles ahead of C++ and Java in terms of expressibility and close in terms of performance. It is also relatively simple and has a straightforward interaction with Linux system calls.
The main drawback is also its strength - the garbage collector. vtocc has made spot optimizations to minimize most of the adverse effects of Go’s stop-the-world gc. At this point, we are trading some amount of performance for greater creativity and efficiency at lower layers. unless you’re trying to max out on qps for your servers, you should see acceptable performance from vtocc. Also, go’s garbage collector is being improved. So, this should only get better over time. Go’s existing mark-and-sweep garbage collector is sub-optimal for systems that use large amounts of static memory (like caches). In the case of vtocc, this would be the row cache. To alleviate this, we intend to use memcache for the time being. If the gc ends up addressing this, it should be fairly trivial to switch to an in-memory row cache. Note that the row cache functionality is not fully ready yet."
How can this be when Go lacks:
- enumerations
- exceptions
- generics
- dynamic loading
The short compilation times are possible in any language with modules.
Channels are available as part of concurrency libraries in Java, .NET, C++ and Erlang.
Goroutines are also possible in other languages, in form of continuations or task pools.
- It has an "exception" mechanism that is rarely used, in favour of error-valued returns. (http://blog.golang.org/2010/08/defer-panic-and-recover.html)
- Apparently, vitess didn't need generics. You'll notice a bit of casting here in their implementation of an LRU cache, but writing containers isn't exactly the main purpose of the library. I do admit I want generics, for the sole purpose of stopping people parroting that criticism without actually using the language. (http://code.google.com/p/vitess/source/browse/go/cache/lru_c...)
- Not sure what you mean here. If you mean a Go library is being loaded, then there's an issue for that, but one unlikely to be fixed in the short-term because of Go's unusual calling convention. If you mean Go loading a library at runtime, I don't know why you'd want this. Either way, static linking seems cleaner and more self contained to me.
"Short compilation times" being available in any language is total bull. Try building chromium or firefox in a reasonable amount of time, without a build cluster. Don't you think if there was a way to speed that up, the developers would make that priority 1?
Channel/Goroutines are of course available in all languages, Turing-completeness implies that they must. However, in practice, does all code in those languages make use of the same concurrency primitives? Do they read as nicely as Go does?
He wrote "in any language with modules". C++ doesn't have them.
I am not sure what it is up with these kids today, that only seem to know C and C++ as languages for native development.
There are many others out there that always had modules and fast compilation times.
Go has plenty of support for global constant values that take the place of enumerations, even to the point of having an "iota" syntax to make initializing constants easier.
Exceptions are generally an anti-pattern in my opinion: a hack to get around the ability to return multiple values from a function, so that error conditions can be handled in-band. The other technical challenge exceptions sometimes provide -- guaranteed cleanup after code that might fail to execute -- can be handled by Go's deferred function execution.
Generics are a powerful abstraction, but Go has very good support for (un)boxed values and interfaces, so the only thing you would get from generics is a bit more type safety. The additional complexity required to support generics isn't worth it.
Dynamic loading is arguably a blocker for certain modes of development and distribution, but by not supporting dynamic loading, Go can make tremendous simplifications in its module system. Making the assumption that every go library is distributed in source form greatly reduces compatibility friction and runtime bugs. "DLL Hell" may be a "solved" problem on most systems nowadays, but there are a lot of moving parts to enable that.
The simplifications that Go makes are 100% worth it in my opinion. There is one place where Go sacrifices something that can't be replaced is that a stop-the-world garbage collector may be unacceptable for some applications. Other than that, codebases aren't actually served by having too much power in their languages, even if an individual programmer might be.
Even C has enumerations, a language developed in 1972! This is how modern Go is.
The lack of generics makes everyone that writes generic data structure code like it was the 90's again. Copy-pasting code or writing template processors to generate code. Talk about evolution.
Dynamic loading is an important way to write modular applications that can be composed on run-time. Something like Eclipse would be impossible to write in Go, due to performance constrains of interprocess communication.
If Go did not had Google behind it, I doubt it would be noticed, it would fail as just another language.
Look how successful Limbo and Alef were. And Go is nothing more than a reinvention of them.
A neat example is using it to define constants for a bit field:
const (
A = 1 << iota
B
C
)Each package has a global namespace, so each const has to be unique at the package level to avoid namespace collisions.
With proper enumerations, only the enumeration needs to be unique, while the enumeration elements would be scoped to the enumeration level, as most (not all) languages do.
Holding up Eclipse as an example citing performance is perhaps not the best idea. And Chromium seems to do just fine (performance-wise) using IPC between renderer processes and the main process.
What people fail to see is that Chromium is just one use case.
An IDE for example, would explode memory wise if every single plugin would be a separate process. Then all plugins that require real time interaction with source code manipulation would suffer from heavy context switching.
Sure, if you do it stupidly, and have a highlighter process per open document or something, it could add up over time. But if you share highlighter processes between documents, you have 1 process that you keep open for the lifetime of the application. A simple HTTP server in Go takes < 5 MB of RAM in its steady state, a server using domain sockets would probably be comparable. And even better, a bug in the highlighter doesn't mean your main application crashes. If it crashes, the parent merely restarts the process. Even better, its more secure, in the sense that you can run your highlighter with absolutely no OS privileges. Also, this allows for plugins written in any language that supports sockets.
All I'm saying is that there's no evidence that IPC isn't performant enough. And IPC comes with security, compatibility, and isolation advantages, to boot.
Note that I'm definitely not saying that dynamic loading wouldn't be nice. Do I miss it, when working with Go? Not for my use cases. "Plugins" for servers don't really make sense. But dismissing Go because it doesn't target every use-case at the moment is somewhat shortsighted.
Many of those do provide more than one plugin, which puts it way above 200 plugins.
The approach one process per plugin won't scale in such cases.
The lack of IPC performance for heavy communication, like it happens with plugins, is the main reason why microkernel based OS are yet to become mainstream.
This discussion is getting a little off track.
I'm simply saying that dismissing Go for not supporting plugins is rather illogical, as there are a lot of use cases that have no need of plugins. Servers being a good example of one of these use cases.
Production ready != done.
While it is a bummer, I'd argue that the OP is the one that does not define a trend.
Of course, the 2GB limit on 32bit Windows programs also seems terribly broken to me.
It seems Windows bashing without any background knowledge is becoming popular again -_-
i'd love to hear more about this awesome 32-bit operating system you seem to know about that defies the laws of math.
It argues that while you can get 2Gb (or 3Gb, ...) you cannot get a contiguous space larger than X megabytes.
That said, it is a rather odd limitation of the garbage collector as well. Most GCs works around the problem by being able to allocate memory in chunks that are different. Still - this solution, one large chunk, is by far the simplest and fastest solution to the problem.
I think that a combination of allocating more to userland, killing all the default services and trimming the server, along with telling the memory manager to treat go as a background process would solve this.
http://www.mono-project.com/Mono:Runtime#Mono.27s_use_of_Boe...
Care to explain? I was under the impression that the recent 32-bit memory usage issues are specific to the 8g implementation only.