Erlang/OTP 27.0-rc2 Released

OTP 27.0-rc2

Erlang/OTP 27.0-rc2 is the second release candidate of three before the OTP 27.0 release.

The intention with this release is to get feedback from our users.
All feedback is welcome, even if it is only to say that it works for you.
We encourage users to try it out and give us feedback either by creating an issue at or by posting to Erlang Forums.

All artifacts for the release can be downloaded from the Erlang/OTP Github release
and you can view the new documentation at
You can also install the latest release using kerl like this:

kerl build 27.0-rc2 27.0-rc2.

Erlang/OTP 27 is a new major release with new features, improvements
as well as a few incompatibilities. Some of the new features are
highlighted below.

Many thanks to all contributors!

Highlights for RC2

  • There is a new module json for encoding and decoding JSON.

    Both encoding and decoding can be customized. Decoding can be done in a
    SAX-like fashion and handle multiple documents and streams of data.

    The new json module is used by the jer (JSON Encoding Rules) for ASN.1
    for encoding and decoding JSON. Thus, there is no longer any need to supply
    an external JSON library.

Other notable changes in RC2

  • The existing experimental support for archive files will be changed in a
    future release. The support for having an archive in an escript will remain,
    but the support for using archives in a release will either become more
    limited or completely removed.

    As of Erlang/OTP 27, the function code:lib_dir/2, the -code_path_choice
    flag, and using erl_prim_loader for reading members of an archive are

    To remain compatible with future version of Erlang/OTP escript scripts that
    need to retrieve data files from its archive should use escript:extract/2
    instead of erl_prim_loader and code:lib_dir/2.

  • The order in which the compiler looks up options has changed.

    When there is a conflict in the compiler options given in the -compile()
    attribute and options given to the compiler, the options given in the
    -compile() attribute overrides the option given to the compiler, which in
    turn overrides options given in the ERL_COMPILER_OPTIONS environment


    If some_module.erl has the following attribute:


    and the compiler is invoked like so:

    % erlc +warn_missing_spec some_module.erl

    no warnings will be issued for functions that do not have any specs.

  • configure now automatically enables support for year-2038-safe timestamps.

    By default configure scripts used when building OTP will now try
    to enable support for timestamps that will work after mid-January
    2038. This has typically only been an issue on 32-bit platforms.

    If configure cannot figure out how to enable such timestamps, it
    will abort with an error message. If you want to build the system
    anyway, knowing that the system will not function properly after
    mid-January 2038, you can pass the --disable-year2038 option to
    configure, which will enable configure to continue without
    support for timestamps after mid-January 2038.

Highlights for RC1


EEP-59 has been
implemented. Documentation attributes in source files can now be used
to document functions, types, callbacks, and modules.

The entire Erlang/OTP documentation is now using the new documentation

New language features

  • Triple-Quoted Strings has been implemented as per
    EEP 64 to allow a string
    to encompass a complete paragraph.

  • Adjacent string literals without intervening white space is now a syntax
    error, to avoid possible confusion with triple-quoted strings.

  • Sigils on string literals (both ordinary and triple-quoted) have
    been implemented as per
    EEP 66. For example,
    ~"Björn" or ~b"Björn" are now equivalent to <<"Björn"/utf8>>.

Compiler and JIT improvements

  • The compiler will now merge consecutive updates of the same record.

  • Safe destructive update of tuples has been implemented in the compiler
    and runtime system. This allows the VM to update tuples in-place when it
    is safe to do so, thus improving performance by doing less copying but
    also by producing less garbage.

  • The maybe expression is now enabled by default, eliminating the need
    for enabling the maybe_expr feature.

  • Native coverage support has been implemented in the JIT. It will
    automatically be used by the cover tool to reduce the execution
    overhead when running cover-compiled code. There are also new APIs
    to support native coverage without using the cover tool.

  • The compiler will now raise a warning when updating record/map literals
    to catch a common mistake. For example, the compiler will now emit a
    warning for #r{a=1}#r{b=2}.


  • The erl command now supports the -S flag, which is similar to
    the -run flag, but with some of the rough edges filed off.

  • By default, escripts will now be compiled instead of interpreted. That
    means that the compiler application must be installed.

  • The default process limit has been raised to 1048576 processes.

  • The erlang:system_monitor/2 functionality is now able to monitor long
    message queues in the system.

  • The obsolete and undocumented support for opening a port to an external
    resource by passing an atom (or a string) as first argument to
    open_port(), implemented by the vanilla driver,
    has been removed. This feature has been scheduled for removal in OTP 27
    since the release of OTP 26.

  • The pid field has been removed from erlang:fun_info/1,2.

  • Multiple trace sessions are now supported.


  • Several new functions that accept funs have been added to module timer.

  • The functions is_equal/2, map/2, and filtermap/2 have been added to
    the modules sets, ordsets, and gb_sets.

  • There are new efficient ets traversal functions with guaranteed atomicity.
    For example, ets:next/2 followed by ets:lookup/2 can now be replaced
    with ets:next_lookup/1.

  • The new function ets:update_element/4 is similar to ets:update_element/3,
    but takes a default tuple as the fourth argument, which will be inserted
    if no previous record with that key exists.

  • binary:replace/3,4 now supports using a fun for supplying the
    replacement binary.

  • The new function proc_lib:set_label/1 can be used to add a descriptive
    term to any process that does not have a registered name. The name will
    be shown by tools such as c:i/0 and observer, and it will be included
    in crash reports produced by processes using gen_server, gen_statem,
    gen_event, and gen_fsm.

  • Added functions to retrieve the next higher or lower key/element from
    gb_trees and gb_sets, as well as returning iterators that start at
    given keys/elements.


  • Calls to ct:capture_start/0 and ct:capture_stop/0 are now synchronous to
    ensure that all output is captured.

  • The default CSS will now include a basic dark mode handling if it is
    preferred by the browser.


  • The functions crypto_dyn_iv_init/3 and crypto_dyn_iv_update/3
    that were marked as deprecated in Erlang/OTP 25 have been removed.


  • The --gui option for Dialyzer has been removed.


  • The ssl client can negotiate and handle certificate status request (OCSP
    stapling support on the client side).


  • There is a new tool tprof, which combines the functionality of eprof
    and cprof under one interface. It also adds heap profiling.


  • As an alternative to xmerl_xml, a new export module xmerl_xml_indent
    that provides out-of-the box indented output has been added.

For more details about new features and potential incompatibilities see the README.


When working on riak, which does a lot of binary manipulations, we noticed a performance issue with OTP-27.0-rc2. Turns out that this is also present in first release candidate.

Compared to OTP26 it seems that concatenation of binaries is almost twice as slow.
We wonder whether we overlook something or that this is indeed something that moved into the runtime system.

Anyone else observe this? Hans refined a little test program to exclude random fluctuations:

-compile([export_all, nowarn_export_all]).

test(N) ->
  Bins = [ crypto:strong_rand_bytes(256) || _ <- lists:seq(1, 100) ],
  {T, _} = timer:tc(fun() -> test(Bins, N) end),
  io:format("Took ~.2f ms\n", [T / 1000]).

test(_, 0) ->
test([B0 | Bs] = Bins, N) ->
  X = bin_joins(B0, Bins),
  test([X | Bs], N - 1).

bin_joins(B, []) ->
bin_joins(<<B0:256/binary, _/binary>>, [B1 | Bs]) ->
  bin_joins(<<B1/binary, B0/binary>>, Bs).

If you run:
[ttt:test(100000) || _ <- lists:seq(1, 10)], ok.
on different Erlang versions you can check for yourself.

Anyone noticing this as well?
(I say runtime system, because if I compile code on OTP26 and run on OTP27 or compile on OTP27 and run on OTP26 the OTP26 version is always faster).

Created an issue: Performance degrades in OTP27 on binary concatenation? · Issue #8314 · erlang/otp · GitHub

1 Like

Wow… JSON support came a lot faster than I was expecting!