12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

各種 Web Application Framework メモリ使用量比較調査 (Go, Ruby, Python, Node.js, Java)

Last updated at Posted at 2020-01-05

概要

  • 「Hello World な HTML を返す程度の Web アプリケーション」または「それぞれの公式ドキュメントに載っているチュートリアルの初期状態に近いもの」を作成してメモリ使用量を比較する
  • Web アプリケーションサーバは現時点でよく使われていると思われるものを使用する
  • テンプレートエンジンはフレームワークのデフォルト設定またはフレームワークが推奨しているものを使用する
  • macOS Catalina 上でメモリ使用量を計測
  • メモリ使用量の計測には ps コマンドの RSS 値を使う (複数のプロセスを使うものはRSS値を単純に足したものをメモリ使用量とした。メモリ共有部分については考慮していないため、もう少しメモリ使用量が少ないかもしれない)

調査結果

メモリ使用量が少ない順に並べる。

  • 7MB: Go 1.13 + Gin 1.5
  • 10MB: Go 1.13 + Revel 0.21
  • 24MB: Ruby 2.7 + Sinatra 2.0 + Puma 4.3
  • 40MB: Python 3.8 + Flask 1.1 + Gunicorn 20.0
  • 42MB: Node.js 12.14 + Express 4.16
  • 51MB: Python 3.8 + Django 3.0 + Gunicorn 20.0
  • 80MB: Ruby 2.7 + Ruby on Rails 6.0 + Puma 4.3
  • 167MB: Java 11.0 + Spring Boot 2.2 + Tomcat Embed Core 9.0

計測環境の構築と計測

Go 1.13 + Gin 1.5

main.go
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default()
  router.LoadHTMLGlob("templates/*")
  router.GET("/hello", func(c *gin.Context) {
    c.HTML(http.StatusOK, "hello.tmpl", gin.H{})
  })
  router.Run(":8080")
}
templates/hello.tmpl
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ go build main.go
$ GIN_MODE=release ./main
$ curl http://localhost:8080/hello
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 30713   0.0  0.1  4405168   7220 s000  S+   12:04AM   0:00.03 ./main

Go 1.13 + Revel 0.21

Creating a new Revel application | Revel - A Web Application Framework for Go!

$ revel new myapp

Revel tool | Revel - A Web Application Framework for Go!

$ revel package -a myapp -m prod

Deployment | Revel - A Web Application Framework for Go!

$ tar zxvf myapp.tar.gz
run.sh
#!/bin/sh

SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/myapp" -importPath myapp -srcPath "$SCRIPTPATH/src" -runMode prod
$ ./run.sh 
Revel engine is listening on.. localhost:9000
$ curl http://localhost:9000/
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 32777   0.0  0.1  4405468   8928 s000  S+   12:32AM   0:00.04 /Users/hoge/go/myapp
hoge 32771   0.0  0.0  4288312   1048 s000  S+   12:32AM   0:00.01 /bin/sh ./run.sh

Ruby 2.7 + Sinatra 2.0 + Puma 4.3

  • Sinatra
  • 構成: Ruby 2.7.0 + Sinatra 2.0.8.1 + Erb + Puma 4.3.1
myapp.rb
require 'sinatra'

get '/hello' do
  erb :hello
end
hello.erb
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ APP_ENV=production RACK_ENV=production ruby myapp.rb
== Sinatra (v2.0.8.1) has taken the stage on 4567 for production with backup from Puma
Puma starting in single mode...
* Version 4.3.1 (ruby 2.7.0-p0), codename: Mysterious Traveller
* Min threads: 0, max threads: 16
* Environment: production
* Listening on tcp://0.0.0.0:4567
$ curl http://localhost:4567/hello
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 99121   0.0  0.3  4378332  24540 s002  S+   10:18PM   0:00.43 puma 4.3.1 (tcp://0.0.0.0:4567)

Python 3.8 + Flask 1.1 + Gunicorn 20.0

myapp.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/hello")
def hello():
  return render_template("hello.html")
templates/hello.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ gunicorn myapp:app
[2020-01-04 23:55:18 +0900] [29978] [INFO] Starting gunicorn 20.0.4
[2020-01-04 23:55:18 +0900] [29978] [INFO] Listening at: http://127.0.0.1:8000 (29978)
[2020-01-04 23:55:18 +0900] [29978] [INFO] Using worker: sync
[2020-01-04 23:55:18 +0900] [29995] [INFO] Booting worker with pid: 29995
$ curl http://localhost:8000/hello
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 29995   0.0  0.3  4273780  22632 s000  S+   11:55PM   0:00.37 /Users/hoge/.pyenv
hoge 29978   0.0  0.2  4265996  18532 s000  S+   11:55PM   0:00.46 /Users/hoge/.pyenv

Node.js 12.14 + Express 4.16

Express application generator

$ express --view=pug myapp
$ cd myapp/

$ npm install

$ npm audit fix
$ node ./bin/www
$ curl http://localhost:3000/
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 19537   0.0  0.5  4565284  43228 s000  S+    9:59PM   0:00.83 node ./bin/www

Python 3.8 + Django 3.0 + Gunicorn 20.0

はじめての Django アプリ作成、その 1 | Django ドキュメント | Django

$ django-admin startproject mysite

$ python manage.py startapp polls

mysite/settings.py の設定値の一部を修正。

DEBUG = False

ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]']

# Application definition

INSTALLED_APPS = [
    'polls',

はじめての Django アプリ作成、その 3 | Django ドキュメント | Django

polls/templates/polls/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
polls/views.py
from django.shortcuts import render

def index(request):
  return render(request, 'polls/index.html')
$ gunicorn mysite.wsgi:application
[2020-01-04 23:44:32 +0900] [29172] [INFO] Starting gunicorn 20.0.4
[2020-01-04 23:44:32 +0900] [29172] [INFO] Listening at: http://127.0.0.1:8000 (29172)
[2020-01-04 23:44:32 +0900] [29172] [INFO] Using worker: sync
[2020-01-04 23:44:32 +0900] [29191] [INFO] Booting worker with pid: 29191
$ curl http://localhost:8000/polls/
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 29191   0.0  0.4  4283728  34016 s000  S+   11:44PM   0:00.55 /Users/hoge/.pyenv
hoge 29172   0.0  0.2  4265996  18552 s000  S+   11:44PM   0:00.43 /Users/hoge/.pyenv

Ruby 2.7 + Ruby on Rails 6.0+ Puma 4.3

Getting Started with Rails — Ruby on Rails Guides

$ rails new blog
$ cd blog
$ rails generate controller Welcome index
app/views/welcome/index.html.erb
Hello, World
config/routes.rb
Rails.application.routes.draw do
  get 'welcome/index'
  root 'welcome#index'
end
$ RAILS_ENV=production rails assets:precompile
$ rails server -e production
=> Booting Puma
=> Rails 6.0.2.1 application starting in production 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.7.0-p0), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: production
* Listening on tcp://0.0.0.0:3000
$ curl http://localhost:3000/
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 66416   0.0  1.0  4445976  81964 s000  S+    9:42AM   0:02.97 puma 4.3.1 (tcp://0.0.0.0:3000) [blog]

Java 11.0 + Spring Boot 2.2 + Tomcat Embed Core 9.0

  • Spring Boot
  • 構成: OpenJDK 11.0.2 + Spring Boot 2.2.2 + Spring Web + Thymeleaf 3.0.11 + Tomcat Embed Core 9.0.29
build.gradle
plugins {
  id 'org.springframework.boot' version '2.2.2.RELEASE'
  id 'io.spring.dependency-management' version '1.0.8.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
  implementation 'org.springframework.boot:spring-boot-starter-web'
}
DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootApplication
@Controller
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  @GetMapping("/hello")
  public String hello() {
    return "hello";
  }
}
hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ gradle bootJar

Java は起動直後にメモリを大きく確保してしまうので、最小ヒープサイズに32MBを指定する。

$ java -Xms32m -jar build/libs/demo-0.0.1-SNAPSHOT.jar
$ curl http://localhost:8080/hello
$ ps au
USER   PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
hoge 20294   0.0  2.0  8094612 171024 s000  S+   10:09PM   0:17.35 java -Xms32m -jar build/libs/demo-0.0.1-SNAPSHOT.jar
12
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?