Various Web Application Framework memory usage comparison survey (Go, Ruby, Python, Node.js, Java)

Overview

--Create a "web application that returns Hello World HTML" or "a tutorial that is close to the initial state in each official document" and compare the memory usage. --Use a web application server that seems to be popular at the moment --Use the default settings of the framework or those recommended by the framework for the template engine --Measure memory usage on macOS Catalina --Use the RSS value of the ps command to measure the memory usage (for those using multiple processes, the memory usage is simply the sum of the RSS values. Since the memory shared part is not considered, it is a little more. Memory usage may be low)

Survey results

Arrange in ascending order of memory usage.

Construction and measurement of measurement environment

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

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

Creating your first Django app, part 1|Django documentation| Django

$ django-admin startproject mysite

$ python manage.py startapp polls

Fixed some settings in mysite / settings.py.

DEBUG = False

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

# Application definition

INSTALLED_APPS = [
    'polls',

Creating your first Django app, part 3|Django documentation| 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

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 allocates a large amount of memory immediately after startup, so specify 32MB as the minimum heap size.

$ 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

Recommended Posts

Various Web Application Framework memory usage comparison survey (Go, Ruby, Python, Node.js, Java)
2014 Web Application Framework Trends (PHP / Java / Ruby / Python / Perl)
Django python web framework
Hello World in various languages [Python / PHP / Java / Perl / Ruby]