I have a Rails 2.3.10 app with bundler. At startup the memory footprint is quite big (300MB in development mode).
我有一个与bundler合作的Rails 2.3.10应用。在启动时,内存占用相当大(在开发模式中为300MB)。
I would like to see how much memory each gem takes on startup.
我想看看每个gem在启动时需要多少内存。
3 个解决方案
#1
19
We had a problem in which our basic Rails app, with no traffic or requests, had a footprint of ~140MB on startup.
我们遇到了一个问题,我们的基本Rails应用程序(没有流量或请求)在启动时占用了~140MB的内存。
We used the following approach to trace the memory requirement of each gem specified in the Gemfile of our app, without having to try to patch bundler.
我们使用以下方法跟踪应用程序Gemfile中指定的每个gem的内存需求,而无需尝试对bundler进行补丁。
- using
rails new myappname
generate a new empty rails app - 使用rails new appmyname生成一个新的空rails应用程序
- Copy the Gemfile from the main project to this new rails project
- 将Gemfile从主项目复制到这个新rails项目
- run
bundle install
and thenrails server
to ensure it is possible to boot up the rails server and that any basic configurations required are loaded - 运行bundle install,然后运行rails服务器,以确保能够启动rails服务器,并加载所需的任何基本配置
- Open up the Gemfile and with the exception of the specification for the rails gem, append
require: false
at the end of each line. Ensure that any other gems that are specified with one name but required using :require => 'othergemname' are using the older ruby Hash notation so that the pattern match below will catch it. - 打开Gemfile,除了rails gem的规范之外,在每一行末尾附加require: false。确保用一个名称指定但使用:require => 'othergemname'的任何其他gem都使用旧的ruby哈希表示法,以便下面的模式匹配将捕获它。
- Run
bundle install
again to regenerate the Gemfile.lock - 再次运行bundle install来重新生成Gemfile.lock
-
Create the following script which will use manually require each gem specified in the Gemfile and log the system memory consumed by the rails process before and after.
创建以下脚本,该脚本将使用手工要求Gemfile中指定的每个gem,并记录rails进程在前后消耗的系统内存。
# require_and_profile.rb def require_and_profile(gemname = nil) unless gemname puts "%-20s: %10s | %10s" % ['gem','increment','total'] return end # This is how to get memory of calling process in OS X, check host OS for variants memory_usage = `ps -o rss= -p #{Process.pid}`.to_i / 1024.0 require gemname puts "%-20s: %10.2f | %10.2f" % [ gemname, (`ps -o rss= -p #{Process.pid}`.to_i / 1024.0 - memory_usage), (`ps -o rss= -p #{Process.pid}`.to_i / 1024.0)] end pattern = /^[^#]*gem[ ]*['"]([^,'"]*)['"][ ,~>0-9\.'"]*(:require[ => ]*['"]([^'"]*)['"][, ])?/ require_and_profile File.open('Gemfile').each do |line| if line.match(pattern) if line.match(pattern)[3] require_and_profile line.match(pattern)[3] else require_and_profile line.match(pattern)[1] end end end
-
Run
rails c
运行rails c
load 'require_and_profile.rb'
- 负载的require_and_profile.rb
- The output shows how much (in MB) each gem adds to the base app footprint (increment) and what the total footprint is after inclusion of the gem (total).
- 输出显示了每个gem为基本应用程序增加了多少(以MB为单位)内存占用(增量),以及在包含了gem之后的总内存占用(总量)。
This helped us identify for example, that we'd been requiring asset-sync in our boot when we only needed it in the :asset group. We do find that on different boot-ups the memory footprint of each gem is not exactly the same, but running it a few times does show you the patterns of which ones are the memory-hungry gems.
例如,这帮助我们确定,当我们只需要资产组中的:asset group时,我们在启动时需要资产同步。我们确实发现,在不同的启动时,每个gem的内存足迹并不是完全相同的,但是运行它几次确实可以向您展示哪些模式是需要内存的宝石。
#2
9
There is an easier way to do this now with the derailed gem:
现在有一种更简单的方法来实现这一点:
add to your gemfile:
添加到您的gemfile:
gem 'derailed', group: :development
then on the command line from your apps root:
然后在应用程序根目录的命令行:
bundle exec derailed bundle:mem
This will print out how much memory each gem takes as it's included.
这将打印出每个宝石包含多少内存。
#3
0
I would do it as follows:
我将这样做:
- Find the place within bundler where all the gems are required.
- 找到邦德勒里面需要所有宝石的地方。
- after each gem is required, get the current object count (
c = 0; ObjectSpace.each_object { c += 1 }
) - 在需要每个gem之后,获取当前对象计数(c = 0;ObjectSpace。每个对象{c += 1})
This way you'll see which gem causes the most objects being instantiated and therefore indirectly causes most memory usage.
通过这种方式,您将看到哪些gem导致了大多数对象被实例化,从而间接导致了大多数内存使用。
Two caveats though:
两个问题:
- memory usage is not really linear to number of objects, but you should get a good enough estimate about which gems are the worst offenders
- 内存使用与对象的数量并不是线性的,但是您应该对哪个gem是最坏的攻击者有一个足够好的估计
- as already pointed out: gems might not load all their code when being required, so a gem might causes more memory usage later on when more code is loaded...
- 如前所述:gems可能不会在需要时加载所有代码,因此当加载更多代码时,gem可能会导致更多的内存使用……
#1
19
We had a problem in which our basic Rails app, with no traffic or requests, had a footprint of ~140MB on startup.
我们遇到了一个问题,我们的基本Rails应用程序(没有流量或请求)在启动时占用了~140MB的内存。
We used the following approach to trace the memory requirement of each gem specified in the Gemfile of our app, without having to try to patch bundler.
我们使用以下方法跟踪应用程序Gemfile中指定的每个gem的内存需求,而无需尝试对bundler进行补丁。
- using
rails new myappname
generate a new empty rails app - 使用rails new appmyname生成一个新的空rails应用程序
- Copy the Gemfile from the main project to this new rails project
- 将Gemfile从主项目复制到这个新rails项目
- run
bundle install
and thenrails server
to ensure it is possible to boot up the rails server and that any basic configurations required are loaded - 运行bundle install,然后运行rails服务器,以确保能够启动rails服务器,并加载所需的任何基本配置
- Open up the Gemfile and with the exception of the specification for the rails gem, append
require: false
at the end of each line. Ensure that any other gems that are specified with one name but required using :require => 'othergemname' are using the older ruby Hash notation so that the pattern match below will catch it. - 打开Gemfile,除了rails gem的规范之外,在每一行末尾附加require: false。确保用一个名称指定但使用:require => 'othergemname'的任何其他gem都使用旧的ruby哈希表示法,以便下面的模式匹配将捕获它。
- Run
bundle install
again to regenerate the Gemfile.lock - 再次运行bundle install来重新生成Gemfile.lock
-
Create the following script which will use manually require each gem specified in the Gemfile and log the system memory consumed by the rails process before and after.
创建以下脚本,该脚本将使用手工要求Gemfile中指定的每个gem,并记录rails进程在前后消耗的系统内存。
# require_and_profile.rb def require_and_profile(gemname = nil) unless gemname puts "%-20s: %10s | %10s" % ['gem','increment','total'] return end # This is how to get memory of calling process in OS X, check host OS for variants memory_usage = `ps -o rss= -p #{Process.pid}`.to_i / 1024.0 require gemname puts "%-20s: %10.2f | %10.2f" % [ gemname, (`ps -o rss= -p #{Process.pid}`.to_i / 1024.0 - memory_usage), (`ps -o rss= -p #{Process.pid}`.to_i / 1024.0)] end pattern = /^[^#]*gem[ ]*['"]([^,'"]*)['"][ ,~>0-9\.'"]*(:require[ => ]*['"]([^'"]*)['"][, ])?/ require_and_profile File.open('Gemfile').each do |line| if line.match(pattern) if line.match(pattern)[3] require_and_profile line.match(pattern)[3] else require_and_profile line.match(pattern)[1] end end end
-
Run
rails c
运行rails c
load 'require_and_profile.rb'
- 负载的require_and_profile.rb
- The output shows how much (in MB) each gem adds to the base app footprint (increment) and what the total footprint is after inclusion of the gem (total).
- 输出显示了每个gem为基本应用程序增加了多少(以MB为单位)内存占用(增量),以及在包含了gem之后的总内存占用(总量)。
This helped us identify for example, that we'd been requiring asset-sync in our boot when we only needed it in the :asset group. We do find that on different boot-ups the memory footprint of each gem is not exactly the same, but running it a few times does show you the patterns of which ones are the memory-hungry gems.
例如,这帮助我们确定,当我们只需要资产组中的:asset group时,我们在启动时需要资产同步。我们确实发现,在不同的启动时,每个gem的内存足迹并不是完全相同的,但是运行它几次确实可以向您展示哪些模式是需要内存的宝石。
#2
9
There is an easier way to do this now with the derailed gem:
现在有一种更简单的方法来实现这一点:
add to your gemfile:
添加到您的gemfile:
gem 'derailed', group: :development
then on the command line from your apps root:
然后在应用程序根目录的命令行:
bundle exec derailed bundle:mem
This will print out how much memory each gem takes as it's included.
这将打印出每个宝石包含多少内存。
#3
0
I would do it as follows:
我将这样做:
- Find the place within bundler where all the gems are required.
- 找到邦德勒里面需要所有宝石的地方。
- after each gem is required, get the current object count (
c = 0; ObjectSpace.each_object { c += 1 }
) - 在需要每个gem之后,获取当前对象计数(c = 0;ObjectSpace。每个对象{c += 1})
This way you'll see which gem causes the most objects being instantiated and therefore indirectly causes most memory usage.
通过这种方式,您将看到哪些gem导致了大多数对象被实例化,从而间接导致了大多数内存使用。
Two caveats though:
两个问题:
- memory usage is not really linear to number of objects, but you should get a good enough estimate about which gems are the worst offenders
- 内存使用与对象的数量并不是线性的,但是您应该对哪个gem是最坏的攻击者有一个足够好的估计
- as already pointed out: gems might not load all their code when being required, so a gem might causes more memory usage later on when more code is loaded...
- 如前所述:gems可能不会在需要时加载所有代码,因此当加载更多代码时,gem可能会导致更多的内存使用……