PurgePJ
I have posted this in StackOverflow as well
I have been into a little trouble lately: The memory used by GenServer processes is super high, probably because of large binary leaks.
The problem comes from here: we receive large binaries through the GenServer and we pass them to the consumer, which then interacts with that data. Now, these large binaries are never assigned to a variable and the GC doesn’t go over them.
I have tried hibernating the processes after managing the data, which partially worked because the memory used by processes lowered a lot, but since binaries were not getting GC’d, the amount of memory used by them increased slowly but steadily, from 30 MBs without hibernating to 200MBs with process hibernation in about 25 minutes.
I have also tried to set :erlang.system_flag(:fullsweep_after, 0), which has also worked and lowered the memory used by processes by around 20%.
Before and after.
I must say it goes down to 60-70MB used by processes from time to time.
Edit: Using :recon.bin_leak(15) frees a lot of memory – result of :recon.bin_leak(15)
Anyhow the memory used is still high and I’m completely sure it can be fixed.
Here you have a screenshot taken from the observer in the Processes tab. As you can see, GenServer is the one eating the memory like the cookie monster.
I have researched a lot about this topic, tried all the suggestions and possible solutions that were given out there, and nevertheless, I am still in this position.
Any help is welcome.
The code is in this Github Repository
Code of interest that is probably causing this + Applications tree. 3 out of 4 processes there (<0.294.0>, <0.295.0>, <0.297.0> are using 27MB of memory.
Thank you beforehand for reading.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex











Showing Posts 18 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
dimitarvp
OMG, I have been reading it as
mapall the time! Editing post.dom
Enum.eachis the right thing to use when you only care about side-effects, it always returns:okand doesn’t accumulate a list.https://github.com/elixir-lang/elixir/blob/v1.7.3/lib/elixir/lib/enum.ex#L764
dimitarvp
EDIT: Ignore the
Enum.eachcomments, I mistook it formap. My bad.First commit:
Poison.Jasonis the newer library and is faster.Second commit: if you are not interested in return value of
Enum.eachI suggest the following instead.Change all these:
to these:
Sure it’s little longer but (1) it does not create a return value and does not pass and modify it between each iteration only for it to eventually be discarded, and (2) communicates the intent of the code better: you are not interested in the results of the loop, you only invoke it for the side effects.
Do this everywhere you do NOT assign anything to the
Enum.eachcalls.PurgePJ
Would it be something like this + this?
aseigo
no problems; it’s really nice to see people taking on projects like yours with Elixir! best of luck and happy hacking …
PurgePJ
I am at work right now so I can’t test all this, but I will definitely try every single suggestion.
Thank you very much for such answer
aseigo
It looks like there are a number of improvements that could be made so your stages run smoother.
For instance, in Coxir.Struct you are using ets as an in-memory backing store and have these
encode/1anddecode/1functions. Those are getting used a lot and just change a Map into a list to a tuple and back… It looks like you are doing this so you can store it in an ets table which needs tuples .. but .. you could just do:Then you will have equivalent lookups:
No moving data between different data structures constantly. Whether or not that is causing your memory usage (doubtful), it will certainly relieve some pressure on the GC and make your code faster.
In
Coxir.Stage.Middle.handle/2there are many places where you are usingforlist comprehensions, but discarding the results.forcreates a list out of every single result .. if you do not use those results, theforis storing them in a list for no good reason. What you probably want instead isEnum.each/2which just calls the function for each entry in the enumerable, IOW: for the side-effects. List comprehensions are there to build lists, not create side effects.More plainly:
forin Elixir is not equivalent to a for-loop in your typical imperative language.Enum.each/2is closer to loops that do not change external variables, andEnum.reduce/3is closer to those that do.There is also interesting code in Consumer like:
where you could just do:
It’s equivalent and probably easier to read, esp as you can then just do:
which is, at least IMHO, a lot easier to read than the pipeline syntax used there
It contains lal the information needed in one line (e.g. what is the subject of the case) rather than having to track back…
I also noticed you have call of
You should really look to replace that approach.
String.to_atom/1on externally supplied data .. this is a great way to exhaust the atom table (you only get so many!), run out of entries, and DoS your app as an unhappy side-effectFinally .. as to where your binary “leaks” are coming from … I assume it is something like this:
You can get around this by calling
:binary.copy/1on the specific sub-strings you are storing in the ets table to see if that is indeed the issue.PurgePJ
Honestly, not yet. I managed to lower the memory used by the entire module by reducing the amount of middles, but the main problem was, and still is, the memory / binary leak from GenServer.
The issue is that GenServer processes are consuming way too much memory, sadly.
dimitarvp
Did you end up solving your problem? Quite an interesting thread.
PurgePJ
Very interesting, will really have a look at it. I will later post here the results