<meta charset="utf-8">
<link crossorigin="anonymous" media="all" integrity="sha512-TtA9plNgcRtWEQtrabLyi5AfjZoT6h2cH0fHNbaLGq0hVhLeAbke8639lygQahMpdj4FHZ/P7HRjUrhM7SKY2Q==" rel="stylesheet" href="https://github.githubassets.com/assets/github-bd06d35642be41f7a7b291a8c9923889.css" />
<meta name="description" content="Java binary serialization and cloning: fast, efficient, automatic - EsotericSoftware/kryo">
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
<meta name="twitter:image:src" content="https://avatars1.githubusercontent.com/u/3641958?s=400&v=4" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary" /><meta name="twitter:title" content="EsotericSoftware/kryo" /><meta name="twitter:description" content="Java binary serialization and cloning: fast, efficient, automatic - EsotericSoftware/kryo" />
<meta property="og:image" content="https://avatars1.githubusercontent.com/u/3641958?s=400&v=4" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="EsotericSoftware/kryo" /><meta property="og:url" content="https://github.com/EsotericSoftware/kryo" /><meta property="og:description" content="Java binary serialization and cloning: fast, efficient, automatic - EsotericSoftware/kryo" />
<meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
<meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
<meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc">
<meta name="google-analytics" content="UA-3769691-2">
<meta name="hostname" content="github.com">
<meta name="user-login" content="">
<meta name="expected-hostname" content="github.com">
<meta name="js-proxy-site-detection-payload" content="MTBjMzUxNWFhNmMwY2ZhMTA4MjQzNmViNzZhMzNhMTFlYmEyYzU0MWUxNzM0Yjc1ZjQyMmY4YjUxN2YwY2U2OXx7InJlbW90ZV9hZGRyZXNzIjoiMjcuMTU0Ljc0LjIwNSIsInJlcXVlc3RfaWQiOiIwQUQxOjY4Rjc6NkZFQjgyOkExRDkzQjo1RDQ1MjUwRiIsInRpbWVzdGFtcCI6MTU2NDgxMjU2MCwiaG9zdCI6ImdpdGh1Yi5jb20ifQ==">
<meta name="enabled-features" content="MARKETPLACE_FEATURED_BLOG_POSTS,MARKETPLACE_INVOICED_BILLING,MARKETPLACE_SOCIAL_PROOF_CUSTOMERS,MARKETPLACE_TRENDING_SOCIAL_PROOF,MARKETPLACE_RECOMMENDATIONS,MARKETPLACE_PENDING_INSTALLATIONS">
<link href="https://github.com/EsotericSoftware/kryo/commits/master.atom" rel="alternate" title="Recent Commits to kryo:master" type="application/atom+xml">
<link rel="canonical" href="https://github.com/EsotericSoftware/kryo/blob/master/README.md" data-pjax-transient>
Skip to content
<div id="js-pjax-loader-bar" class="pjax-loader-bar"><div class="progress"></div></div>
<header class="Header-old header-logged-out js-details-container Details position-relative f4 py-2" role="banner">
<div class="d-flex flex-justify-between flex-items-center">
<a class="mr-4" href="https://github.com/" aria-label="Homepage" data-ga-click="(Logged out) Header, go to homepage, icon:logo-wordmark">
<svg height="32" class="octicon octicon-mark-github text-white" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
</a>
<div class="d-lg-none css-truncate css-truncate-target width-fit p-2">
<svg class="octicon octicon-repo" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
<a class="Header-link" href="/EsotericSoftware">EsotericSoftware</a>
/
<a class="Header-link" href="/EsotericSoftware/kryo">kryo</a>
</div>
<div class="d-flex flex-items-center">
<a href="/join?source=header-repo"
class="d-inline-block d-lg-none f5 text-white no-underline border border-gray-dark rounded-2 px-2 py-1 mr-3 mr-sm-5"
data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header","repository_id":null,"auth_type":"SIGN_UP","client_id":null,"originating_request_id":"0AD1:68F7:6FEB82:A1D93B:5D45250F","originating_url":"https://github.com/EsotericSoftware/kryo/blob/master/README.md","referrer":null,"user_id":null}}" data-hydro-click-hmac="b6181806160be15dc4666cbacb10f00bea8fe87aceba0426dd5d6c32384244e9"
data-ga-click="(Logged out) Header, clicked Sign up, text:sign-up">
Sign up
</a>
<button class="btn-link d-lg-none mt-1 js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
<svg height="24" class="octicon octicon-three-bars text-white" viewBox="0 0 12 16" version="1.1" width="18" aria-hidden="true"><path fill-rule="evenodd" d="M11.41 9H.59C0 9 0 8.59 0 8c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zm0-4H.59C0 5 0 4.59 0 4c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zM.59 11H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1H.59C0 13 0 12.59 0 12c0-.59 0-1 .59-1z"/></svg>
</button>
</div>
</div>
<div class="HeaderMenu HeaderMenu--logged-out position-fixed top-0 right-0 bottom-0 height-fit position-lg-relative d-lg-flex flex-justify-between flex-items-center flex-auto">
<div class="d-flex d-lg-none flex-justify-end border-bottom bg-gray-light p-3">
<button class="btn-link js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
<svg height="24" class="octicon octicon-x text-gray" viewBox="0 0 12 16" version="1.1" width="18" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg>
</button>
</div>
<nav class="mt-0 px-3 px-lg-0 mb-5 mb-lg-0" aria-label="Global">
<ul class="d-lg-flex list-style-none">
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Why GitHub?
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<a href="/features" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Features">Features <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a>
<ul class="list-style-none f5 pb-3">
<li class="edge-item-fix"><a href="/features/code-review/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code review">Code review</a></li>
<li class="edge-item-fix"><a href="/features/project-management/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Project management">Project management</a></li>
<li class="edge-item-fix"><a href="/features/integrations" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Integrations">Integrations</a></li>
<li class="edge-item-fix"><a href="/features/actions" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Actions">Actions</a>
<li class="edge-item-fix"><a href="/features/package-registry" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Package Registry">Package registry</a>
<li class="edge-item-fix"><a href="/features#team-management" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Team management">Team management</a></li>
<li class="edge-item-fix"><a href="/features#social-coding" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Social coding">Social coding</a></li>
<li class="edge-item-fix"><a href="/features#documentation" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Documentation">Documentation</a></li>
<li class="edge-item-fix"><a href="/features#code-hosting" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code hosting">Code hosting</a></li>
</ul>
<ul class="list-style-none mb-0 border-lg-top pt-lg-3">
<li class="edge-item-fix"><a href="/customer-stories" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Customer stories">Customer stories <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li>
<li class="edge-item-fix"><a href="/security" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Security">Security <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li>
</ul>
</div>
</details>
</li>
<li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
<a href="/enterprise" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Enterprise">Enterprise</a>
</li>
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Explore
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/explore" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Explore">Explore GitHub <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li>
</ul>
<h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Learn & contribute</h4>
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/topics" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Topics">Topics</a></li>
<li class="edge-item-fix"><a href="/collections" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Collections">Collections</a></li>
<li class="edge-item-fix"><a href="/trending" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Trending">Trending</a></li>
<li class="edge-item-fix"><a href="https://lab.github.com/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Learning lab">Learning Lab</a></li>
<li class="edge-item-fix"><a href="https://opensource.guide" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Open source guides">Open source guides</a></li>
</ul>
<h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Connect with others</h4>
<ul class="list-style-none mb-0">
<li class="edge-item-fix"><a href="https://github.com/events" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Events">Events</a></li>
<li class="edge-item-fix"><a href="https://github.community" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Community forum">Community forum</a></li>
<li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Education">GitHub Education</a></li>
</ul>
</div>
</details>
</li>
<li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
<a href="/marketplace" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Marketplace">Marketplace</a>
</li>
<li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
<details class="HeaderMenu-details details-overlay details-reset width-full">
<summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
Pricing
<svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
<path d="M1,1l6.2,6L13,1"></path>
</svg>
</summary>
<div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-4 mt-0 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
<a href="/pricing" class="pb-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Pricing">Plans <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a>
<ul class="list-style-none mb-3">
<li class="edge-item-fix"><a href="/pricing#feature-comparison" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Compare plans">Compare plans</a></li>
<li class="edge-item-fix"><a href="https://enterprise.github.com/contact" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Contact Sales">Contact Sales</a></li>
</ul>
<ul class="list-style-none mb-0 border-lg-top pt-lg-3">
<li class="edge-item-fix"><a href="/nonprofit" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Nonprofits">Nonprofit <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li>
<li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Education">Education <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li>
</ul>
</div>
</details>
</li>
</ul>
</nav>
<div class="d-lg-flex flex-items-center px-3 px-lg-0 text-center text-lg-left">
<div class="d-lg-flex mb-3 mb-lg-0">
<div class="header-search flex-self-stretch flex-lg-self-auto mr-0 mr-lg-3 mb-3 mb-lg-0 scoped-search site-scoped-search js-site-search position-relative js-jump-to"
role=“combobox”
aria-owns=“jump-to-results”
aria-label=“Search or jump to”
aria-haspopup=“listbox”
aria-expanded=“false”
<label class="form-control input-sm header-search-wrapper p-0 header-search-wrapper-jump-to position-relative d-flex flex-justify-between flex-items-center js-chromeless-input-container">
<input type="text"
class="form-control input-sm header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable"
data-hotkey="s,/"
name="q"
value=""
placeholder="Search"
data-unscoped-placeholder="Search GitHub"
data-scoped-placeholder="Search"
autocapitalize="off"
aria-autocomplete="list"
aria-controls="jump-to-results"
aria-label="Search"
data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations#csrf-token=LQcfuwmiBeByMj9Z6dP4V/qeHW9jqjRq3THOY8B/fx2U3EkMPlcKcCyVwOuaD2tlvTAfohE8oxtktG9lz7POKg=="
spellcheck="false"
autocomplete="off"
>
<input type="hidden" class="js-site-search-type-field" name="type" >
<div class="Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container">
-
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg> <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1V1a1 1 0 0 0-1-1z"/></svg> <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg> </div> <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> In this repository </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
-
<span class="text-gray">No suggested jump to results</span>
-
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg> <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1V1a1 1 0 0 0-1-1z"/></svg> <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg> </div> <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> In this repository </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
-
<div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 12 16" version="1.1" role="img"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg> <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 15 16" version="1.1" role="img"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1V1a1 1 0 0 0-1-1z"/></svg> <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"/></svg> </div> <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> In this repository </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
</div> </label>
</div> <a href="/login?return_to=%2FEsotericSoftware%2Fkryo%2Fblob%2Fmaster%2FREADME.md" class="HeaderMenu-link no-underline mr-3" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","client_id":null,"originating_request_id":"0AD1:68F7:6FEB82:A1D93B:5D45250F","originating_url":"https://github.com/EsotericSoftware/kryo/blob/master/README.md","referrer":null,"user_id":null}}" data-hydro-click-hmac="2cd7f07eb0c795f550661292b3507c5b3ae53bca685b989b15ea35a0072b3a51" data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in"> Sign in </a> <a href="/join?source=header-repo" class="HeaderMenu-link d-inline-block no-underline border border-gray-dark rounded-1 px-2 py-1" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","client_id":null,"originating_request_id":"0AD1:68F7:6FEB82:A1D93B:5D45250F","originating_url":"https://github.com/EsotericSoftware/kryo/blob/master/README.md","referrer":null,"user_id":null}}" data-hydro-click-hmac="2cd7f07eb0c795f550661292b3507c5b3ae53bca685b989b15ea35a0072b3a51" data-ga-click="(Logged out) Header, clicked Sign up, text:sign-up"> Sign up </a> </div> </div>
<div id="js-flash-container">
<div itemscope itemtype="http://schema.org/SoftwareSourceCode" class=""> <main >
-
<svg class="octicon octicon-eye v-align-text-bottom" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z"/></svg> Watch
<a class=“social-count” href="/EsotericSoftware/kryo/watchers"
aria-label="311 users are watching this repository"> 311 </a>
-
<a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to star a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":14173176,"auth_type":"LOG_IN","client_id":null,"originating_request_id":"0AD1:68F7:6FEB82:A1D93B:5D45250F","originating_url":"https://github.com/EsotericSoftware/kryo/blob/master/README.md","referrer":null,"user_id":null}}" data-hydro-click-hmac="5ff326ef3a91a5e088a269e72d8c960e669bdccb952ce805c1b7458625bee972" href="/login?return_to=%2FEsotericSoftware%2Fkryo"> <svg class="octicon octicon-star v-align-text-bottom" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z"/></svg> Star
<a class="social-count js-social-count" href="/EsotericSoftware/kryo/stargazers" aria-label="4195 users starred this repository"> 4,195 </a>
-
<a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to fork a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"repo details fork button","repository_id":14173176,"auth_type":"LOG_IN","client_id":null,"originating_request_id":"0AD1:68F7:6FEB82:A1D93B:5D45250F","originating_url":"https://github.com/EsotericSoftware/kryo/blob/master/README.md","referrer":null,"user_id":null}}" data-hydro-click-hmac="073378abf724b86810e27beb8d6d0335d805f686a08055743fe355eb29cc4d85" href="/login?return_to=%2FEsotericSoftware%2Fkryo"> <svg class="octicon octicon-repo-forked v-align-text-bottom" viewBox="0 0 10 16" version="1.1" width="10" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 1a1.993 1.993 0 0 0-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V6.5l3 3v1.78A1.993 1.993 0 0 0 5 15a1.993 1.993 0 0 0 1-3.72V9.5l3-3V4.72A1.993 1.993 0 0 0 8 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg> Fork
<a href="/EsotericSoftware/kryo/network/members" class="social-count" aria-label="675 users forked this repository"> 675 </a>
<h1 class="public "> <svg class="octicon octicon-repo" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
<!–
–>/<!–
–>kryo
</div>
<a class="js-selected-navigation-item selected reponav-item" itemprop="url" data-hotkey="g c" aria-current="page" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages /EsotericSoftware/kryo" href="/EsotericSoftware/kryo"> <svg class="octicon octicon-code" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M9.5 3L8 4.5 11.5 8 8 11.5 9.5 13 14 8 9.5 3zm-5 0L0 8l4.5 5L6 11.5 2.5 8 6 4.5 4.5 3z"/></svg> <span itemprop="name">Code</span> <meta itemprop="position" content="1">
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement"> <a itemprop="url" data-hotkey="g i" class="js-selected-navigation-item reponav-item" data-selected-links="repo_issues repo_labels repo_milestones /EsotericSoftware/kryo/issues" href="/EsotericSoftware/kryo/issues"> <svg class="octicon octicon-issue-opened" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"/></svg> <span itemprop="name">Issues</span> <span class="Counter">17</span> <meta itemprop="position" content="2">
<a data-hotkey="g p" itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_pulls checks /EsotericSoftware/kryo/pulls" href="/EsotericSoftware/kryo/pulls"> <svg class="octicon octicon-git-pull-request" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg> <span itemprop="name">Pull requests</span> <span class="Counter">2</span> <meta itemprop="position" content="3">
<a class="js-selected-navigation-item reponav-item" data-hotkey="g w" data-selected-links="repo_wiki /EsotericSoftware/kryo/wiki" href="/EsotericSoftware/kryo/wiki"> <svg class="octicon octicon-book" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3 5h4v1H3V5zm0 3h4V7H3v1zm0 2h4V9H3v1zm11-5h-4v1h4V5zm0 2h-4v1h4V7zm0 2h-4v1h4V9zm2-6v9c0 .55-.45 1-1 1H9.5l-1 1-1-1H2c-.55 0-1-.45-1-1V3c0-.55.45-1 1-1h5.5l1 1 1-1H15c.55 0 1 .45 1 1zm-8 .5L7.5 3H2v9h6V3.5zm7-.5H9.5l-.5.5V12h6V3z"/></svg> Wiki
<a data-skip-pjax="true" class="js-selected-navigation-item reponav-item" data-selected-links="security alerts policy /EsotericSoftware/kryo/security/advisories" href="/EsotericSoftware/kryo/security/advisories"> <svg class="octicon octicon-shield" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M0 2l7-2 7 2v6.02C14 12.69 8.69 16 7 16c-1.69 0-7-3.31-7-7.98V2zm1 .75L7 1l6 1.75v5.268C13 12.104 8.449 15 7 15c-1.449 0-6-2.896-6-6.982V2.75zm1 .75L7 2v12c-1.207 0-5-2.482-5-5.985V3.5z"/></svg> Security
<a class="js-selected-navigation-item reponav-item" data-selected-links="repo_graphs repo_contributors dependency_graph pulse people /EsotericSoftware/kryo/pulse" href="/EsotericSoftware/kryo/pulse"> <svg class="octicon octicon-graph" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M16 14v1H0V0h1v14h15zM5 13H3V8h2v5zm4 0H7V3h2v10zm4 0h-2V6h2v7z"/></svg> Insights
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement"> <a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_issues repo_labels repo_milestones /EsotericSoftware/kryo/issues" href="/EsotericSoftware/kryo/issues"> <span itemprop="name">Issues</span> <span class="Counter">17</span> <meta itemprop="position" content="2">
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement"> <a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_pulls checks /EsotericSoftware/kryo/pulls" href="/EsotericSoftware/kryo/pulls"> <span itemprop="name">Pull requests</span> <span class="Counter">2</span> <meta itemprop="position" content="3">
<span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement"> <a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="repo_wiki /EsotericSoftware/kryo/wiki" href="/EsotericSoftware/kryo/wiki"> <span itemprop="name">Wiki</span> <meta itemprop="position" content="5">
<a itemprop="url" class="js-selected-navigation-item reponav-item" data-selected-links="security alerts policy /EsotericSoftware/kryo/security/advisories" href="/EsotericSoftware/kryo/security/advisories"> <span itemprop="name">Security</span> <meta itemprop="position" content="6">
<a class="js-selected-navigation-item reponav-item" data-selected-links="pulse /EsotericSoftware/kryo/pulse" href="/EsotericSoftware/kryo/pulse"> Pulse
<a class="d-none js-permalink-shortcut" data-hotkey="y" href="/EsotericSoftware/kryo/blob/8d8deb6a3a628631f97b93fd706987f84224c098/README.md">Permalink</a> <!-- blob contrib key: blob_contributors:v21:96788de59dc12e26dc60effda31936bc --> <div class="signup-prompt-bg rounded-1"> <div class="signup-prompt p-4 text-center mb-4 rounded-1"> <div class="position-relative"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form action="/prompt_dismissals/signup" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="_method" value="put" /><input type="hidden" name="authenticity_token" value="LKtD4pptXdh3SuMvueIEThREmPjAE3lj77VvAYH0uQ6XMyrcWydax0uTZ62HnXDGSOGfN/8fIii2/VG4kFcixw==" /> <button type="submit" class="position-absolute top-0 right-0 btn-link link-gray" data-ga-click="(Logged out) Sign up prompt, clicked Dismiss, text:dismiss"> Dismiss </button>
Join GitHub today
<p class="col-6 mx-auto">GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.</p> Sign up </div> </div> </div> <div class="d-flex flex-items-start flex-shrink-0 mb-2 flex-column flex-md-row"> <span class="d-flex flex-justify-between width-full width-md-auto">
<include-fragment class="select-menu-loading-overlay anim-pulse"> <svg height="32" class="octicon octicon-octoface" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M14.7 5.34c.13-.32.55-1.59-.13-3.31 0 0-1.05-.33-3.44 1.3-1-.28-2.07-.32-3.13-.32s-2.13.04-3.13.32c-2.39-1.64-3.44-1.3-3.44-1.3-.68 1.72-.26 2.99-.13 3.31C.49 6.21 0 7.33 0 8.69 0 13.84 3.33 15 7.98 15S16 13.84 16 8.69c0-1.36-.49-2.48-1.3-3.35zM8 14.02c-3.3 0-5.98-.15-5.98-3.35 0-.76.38-1.48 1.02-2.07 1.07-.98 2.9-.46 4.96-.46 2.07 0 3.88-.52 4.96.46.65.59 1.02 1.3 1.02 2.07 0 3.19-2.68 3.35-5.98 3.35zM5.49 9.01c-.66 0-1.2.8-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.54-1.78-1.2-1.78zm5.02 0c-.66 0-1.2.79-1.2 1.78s.54 1.79 1.2 1.79c.66 0 1.2-.8 1.2-1.79s-.53-1.78-1.2-1.78z"/></svg> </include-fragment>
<div class="BtnGroup flex-shrink-0 d-md-none"> <a href="/EsotericSoftware/kryo/find/master" class="js-pjax-capture-input btn btn-sm BtnGroup-item" data-pjax data-hotkey="t"> Find file </a> <clipboard-copy value="README.md" class="btn btn-sm BtnGroup-item"> Copy path </clipboard-copy> </div> </span> <h2 id="blob-path" class="breadcrumb flex-auto min-width-0 text-normal flex-md-self-center ml-md-2 mr-md-3 my-2 my-md-0"> <span class="js-repo-root text-bold"><span class="js-path-segment"><a data-pjax="true" href="/EsotericSoftware/kryo"><span>kryo</span></a></span></span><span class="separator">/</span><strong class="final-path">README.md</strong> </h2> <div class="BtnGroup flex-shrink-0 d-none d-md-inline-block"> <a href="/EsotericSoftware/kryo/find/master" class="js-pjax-capture-input btn btn-sm BtnGroup-item" data-pjax data-hotkey="t"> Find file </a> <clipboard-copy value="README.md" class="btn btn-sm BtnGroup-item"> Copy path </clipboard-copy> </div> </div>
<div class="Box-body d-flex flex-justify-between bg-blue-light flex-column flex-md-row flex-items-start flex-md-items-center"> <span class="pr-md-4 f6"> magro <span class="lh-default v-align-middle"> Update README for RC4 </span> </span> <span class="d-inline-block flex-shrink-0 v-align-bottom f6 mt-2 mt-md-0"> a8be1ab <relative-time datetime="2019-04-14T19:40:18Z">Apr 14, 2019</relative-time> </span> </div> <div class="Box-body d-flex flex-items-center flex-auto f6 border-bottom-0 flex-wrap" > <details class="details-reset details-overlay details-overlay-dark lh-default text-gray-dark float-left mr-2" id="blob_contributors_box"> <summary class="btn-link" aria-haspopup="dialog"> <span><strong>24</strong> contributors</span> </summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast" aria-label="Users who have contributed to this file" src="/EsotericSoftware/kryo/contributors/master/README.md/list" preload> <div class="Box-header"> <button class="Box-btn-octicon btn-octicon float-right" type="button" aria-label="Close dialog" data-close-dialog> <svg class="octicon octicon-x" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg> </button> <h3 class="Box-title"> Users who have contributed to this file </h3> </div> <include-fragment class="octocat-spinner my-3" aria-label="Loading..."></include-fragment> </details-dialog> </details> <span class=""> <a class="avatar-link" data-hovercard-type="user" data-hovercard-url="/hovercards?user_id=434010" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/EsotericSoftware/kryo/commits/master/README.md?author=NathanSweet"> <img class="avatar mr-1" src="https://avatars1.githubusercontent.com/u/434010?s=40&v=4" width="20" height="20" alt="@NathanSweet" />
</div>
<div class="Box mt-3 position-relative">
1277 lines (916 sloc) <span class="file-info-divider"></span> 71.2 KB
<div class="BtnGroup"> <a id="raw-url" class="btn btn-sm BtnGroup-item" href="/EsotericSoftware/kryo/raw/master/README.md">Raw</a> <a class="btn btn-sm js-update-url-with-hash BtnGroup-item" data-hotkey="b" href="/EsotericSoftware/kryo/blame/master/README.md">Blame</a> <a rel="nofollow" class="btn btn-sm BtnGroup-item" href="/EsotericSoftware/kryo/commits/master/README.md">History</a> </div> <div> <a class="btn-octicon tooltipped tooltipped-nw hide-sm" href="https://desktop.github.com" aria-label="Open this file in GitHub Desktop" data-ga-click="Repository, open with desktop, type:windows"> <svg class="octicon octicon-device-desktop" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M15 2H1c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5.34c-.25.61-.86 1.39-2.34 2h8c-1.48-.61-2.09-1.39-2.34-2H15c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm0 9H1V3h14v8z"/></svg> </a> <button type="button" class="btn-octicon disabled tooltipped tooltipped-nw" aria-label="You must be signed in to make or propose changes"> <svg class="octicon octicon-pencil" viewBox="0 0 14 16" version="1.1" width="14" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M0 12v3h3l8-8-3-3-8 8zm3 2H1v-2h1v1h1v1zm10.3-9.3L12 6 9 3l1.3-1.3a.996.996 0 0 1 1.41 0l1.59 1.59c.39.39.39 1.02 0 1.41z"/></svg> </button> <button type="button" class="btn-octicon btn-octicon-danger disabled tooltipped tooltipped-nw" aria-label="You must be signed in to make or propose changes"> <svg class="octicon octicon-trashcan" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11 2H9c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1H2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1v9c0 .55.45 1 1 1h7c.55 0 1-.45 1-1V5c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm-1 12H3V5h1v8h1V5h1v8h1V5h1v8h1V5h1v9zm1-10H2V3h9v1z"/></svg> </button> </div>
<article class="markdown-body entry-content p-3 p-md-6" itemprop="text"><p></p>
Kryo is a fast and efficient binary object graph serialization framework for Java. The goals of the project are high speed, low size, and an easy to use API. The project is useful any time objects need to be persisted, whether to a file, database, or over the network.
Kryo can also perform automatic deep and shallow copying/cloning. This is direct copying from object to object, not object to bytes to object.
Contact / Mailing list
Please use the Kryo mailing list for questions, discussions, and support. Please limit use of the Kryo issue tracker to bugs and enhancements, not questions, discussions, or support.
Table of contents
- Recent releases
- Installation
- With Maven
- Without Maven
- Quickstart
- IO
- Output
- Input
- ByteBuffers
- Unsafe buffers
- Variable length encoding
- Chunked encoding
- Buffer performance
- Reading and writing objects
- Round trip
- Deep and shallow copies
- References
- ReferenceResolver
- Reference limits
- Context
- Reset
- Serializer framework
- Registration
- ClassResolver
- Optional registration
- Default serializers
- Serializer factories
- Object creation
- InstantiatorStrategy
- Overriding create
- Final classes
- Closures
- Compression and encryption
- Implementing a serializer
- Serializer references
- Nested serializers
- KryoException
- Stack size
- Accepting null
- Generics
- KryoSerializable
- Serializer copying
- KryoCopyable
- Immutable serializers
- Kryo versioning and upgrading
- Interoperability
- Compatibility
- Serializers
- FieldSerializer
- CachedField settings
- FieldSerializer annotations
- VersionFieldSerializer
- TaggedFieldSerializer
- CompatibleFieldSerializer
- BeanSerializer
- CollectionSerializer
- MapSerializer
- JavaSerializer and ExternalizableSerializer
- Logging
- Thread safety
- Pooling
- Benchmarks
- Links
- Projects using Kryo
- Scala
- Clojure
- Objective-C
Recent releases
5.0.0-RC4 fourth release candidate with improvements over previous RCs based on feedback. See also Migration to v5 for migration from kryo 4.x.
5.0.0-RC1 fixes many issues and makes many long awaited improvements.
4.0.2 brings several incremental fixes and improvements.
Installation
Kryo JARs are available on the releases page and at Maven Central. The latest snapshots of Kryo, including snapshot builds of master, are in the Sonatype Repository.
With Maven
To use the latest Kryo release, use this dependency entry in your
pom.xml
:dependency
groupIdcom.esotericsoftware/groupId
artifactIdkryo/artifactId
version5.0.0-RC4/version
/dependency
To use the latest Kryo snapshot, use:
repository
idsonatype-snapshots/id
namesonatype snapshots repo/name
urlhttps://oss.sonatype.org/content/repositories/snapshots</url
/repository
dependency
groupIdcom.esotericsoftware/groupId
artifactIdkryo/artifactId
version5.0.0-RC5-SNAPSHOT/version
/dependency
Without Maven
Not everyone is a Maven fan. Using Kryo without Maven requires placing the Kryo JAR on your classpath along with the dependency JARs found in lib.
Quickstart
Jumping ahead to show how the library can be used:
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;
public class HelloKryo {
static public void main (String[] args) throws Exception {
<span class="pl-smi">Kryo</span> kryo <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">Kryo</span>(); kryo<span class="pl-k">.</span>register(<span class="pl-smi">SomeClass</span><span class="pl-k">.</span>class); <span class="pl-smi">SomeClass</span> object <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); object<span class="pl-k">.</span>value <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>Hello Kryo!<span class="pl-pds">"</span></span>; <span class="pl-smi">Output</span> output <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">Output</span>(<span class="pl-k">new</span> <span class="pl-smi">FileOutputStream</span>(<span class="pl-s"><span class="pl-pds">"</span>file.bin<span class="pl-pds">"</span></span>)); kryo<span class="pl-k">.</span>writeObject(output, object); output<span class="pl-k">.</span>close(); <span class="pl-smi">Input</span> input <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">Input</span>(<span class="pl-k">new</span> <span class="pl-smi">FileInputStream</span>(<span class="pl-s"><span class="pl-pds">"</span>file.bin<span class="pl-pds">"</span></span>)); <span class="pl-smi">SomeClass</span> object2 <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readObject(input, <span class="pl-smi">SomeClass</span><span class="pl-k">.</span>class); input<span class="pl-k">.</span>close();
}
static public class SomeClass {
<span class="pl-smi">String</span> value;
}
}
The Kryo class performs the serialization automatically. The Output and Input classes handle buffering bytes and optionally flushing to a stream.
The rest of this document details how this works and advanced usage of the library.
IO
Getting data in and out of Kryo is done using the Input and Output classes. These classes are not thread safe.
Output
The Output class is an OutputStream that writes data to a byte array buffer. This buffer can be obtained and used directly, if a byte array is desired. If the Output is given an OutputStream, it will flush the bytes to the stream when the buffer becomes full, otherwise Output can grow its buffer automatically. Output has many methods for efficiently writing primitives and strings to bytes. It provides functionality similar to DataOutputStream, BufferedOutputStream, FilterOutputStream, and ByteArrayOutputStream, all in one class.
Tip: Output and Input provide all the functionality of ByteArrayOutputStream. There is seldom a reason to have Output flush to a ByteArrayOutputStream.
Output buffers the bytes when writing to an OutputStream, so
flush
orclose
must be called after writing is complete to cause the buffered bytes to be written to the OutputStream. If the Output has not been provided an OutputStream, callingflush
orclose
is unnecessary. Unlike many streams, an Output instance can be reused by setting the position, or setting a new byte array or stream.Tip: Since Output buffers already, there is no reason to have Output flush to a BufferedOutputStream.
The zero argument Output constructor creates an uninitialized Output. Output
setBuffer
must be called before the Output can be used.Input
The Input class is an InputStream that reads data from a byte array buffer. This buffer can be set directly, if reading from a byte array is desired. If the Input is given an InputStream, it will fill the buffer from the stream when all the data in the buffer has been read. Input has many methods for efficiently reading primitives and strings from bytes. It provides functionality similar to DataInputStream, BufferedInputStream, FilterInputStream, and ByteArrayInputStream, all in one class.
Tip: Input provides all the functionality of ByteArrayInputStream. There is seldom a reason to have Input read from a ByteArrayInputStream.
If the Input
close
is called, the Input's InputStream is closed, if any. If not reading from an InputStream then it is not necessary to callclose
. Unlike many streams, an Input instance can be reused by setting the position and limit, or setting a new byte array or InputStream.The zero argument Input constructor creates an uninitialized Input. Input
setBuffer
must be called before the Input can be used.ByteBuffers
The ByteBufferOutput and ByteBufferInput classes work exactly like Output and Input, except they use a ByteBuffer rather than a byte array.
Unsafe buffers
The UnsafeOutput, UnsafeInput, UnsafeByteBufferOutput, and UnsafeByteBufferInput classes work exactly like their non-unsafe counterparts, except they use sun.misc.Unsafe for higher performance in many cases. To use these classes
Util.unsafe
must be true.The downside to using unsafe buffers is that the native endianness and representation of numeric types of the system performing the serialization affects the serialized data. For example, deserialization will fail if the data is written on X86 and read on SPARC. Also, if data is written with an unsafe buffer, it must be read with an unsafe buffer.
The biggest performance difference with unsafe buffers is with large primitive arrays when variable length encoding is not used. Variable length encoding can be disabled for the unsafe buffers or only for specific fields (when using FieldSerializer).
Variable length encoding
The IO classes provide methods to read and write variable length int (varint) and long (varlong) values. This is done by using the 8th bit of each byte to indicate if more bytes follow, which means a varint uses 1-5 bytes and a varlong uses 1-9 bytes. Using variable length encoding is more expensive but makes the serialized data much smaller.
When writing a variable length value, the value can be optimized either for positive values or for both negative and positive values. For example, when optimized for positive values, 0 to 127 is written in one byte, 128 to 16383 in two bytes, etc. However, small negative numbers are the worst case at 5 bytes. When not optimized for positive, these ranges are shifted down by half. For example, -64 to 63 is written in one byte, 64 to 8191 and -65 to -8192 in two bytes, etc.
Input and Output buffers provides methods to read and write fixed sized or variable length values. There are also methods to allow the buffer to decide whether a fixed size or variable length value is written. This allows serialization code to ensure variable length encoding is used for very common values that would bloat the output if a fixed size were used, while still allowing the buffer configuration to decide for all other values.
Method Description writeInt(int) Writes a 4 byte int. writeVarInt(int, boolean) Writes a 1-5 byte int. writeInt(int, boolean) Writes either a 4 or 1-5 byte int (the buffer decides). writeLong(long) Writes an 8 byte long. writeVarLong(long, boolean) Writes an 1-9 byte long. writeLong(long, boolean) Writes either an 8 or 1-9 byte long (the buffer decides).
To disable variable length encoding for all values, the
writeVarInt
,writeVarLong
,readVarInt
, andreadVarLong
methods would need to be overridden.Chunked encoding
It can be useful to write the length of some data, then the data. When the length of the data is not known ahead of time, all the data needs to be buffered to determine its length, then the length can be written, then the data. using a single, large buffer for this would prevent streaming and may require an unreasonably large buffer, which is not ideal.
Chunked encoding solves this problem by using a small buffer. When the buffer is full, its length is written, then the data. This is one chunk of data. The buffer is cleared and this continues until there is no more data to write. A chunk with a length of zero denotes the end of the chunks.
Kryo provides classes to maked chunked encoding. OutputChunked is used to write chunked data. It extends Output, so has all the convenient methods to write data. When the OutputChunked buffer is full, it flushes the chunk to another OutputStream. The
endChunks
method is used to mark the end of a set of chunks.OutputStream outputStream = new FileOutputStream("file.bin");
OutputChunked output = new OutputChunked(outputStream, 1024);
// Write data to output…
output.endChunks();
// Write more data to output…
output.endChunks();
// Write even more data to output…
output.close();
To read the chunked data, InputChunked is used. It extends Input, so has all the convenient methods to read data. When reading, InputChunked will appear to hit the end of the data when it reaches the end of a set of chunks. The
nextChunks
method advances to the next set of chunks, even if not all the data has been read from the current set of chunks.InputStream outputStream = new FileInputStream("file.bin");
InputChunked input = new InputChunked(inputStream, 1024);
// Read data from first set of chunks…
input.nextChunks();
// Read data from second set of chunks…
input.nextChunks();
// Read data from third set of chunks…
input.close();
Buffer performance
Generally Output and Input provide good performance. Unsafe buffers perform as well or better, especially for primitive arrays, if their crossplatform incompatibilities are acceptable. ByteBufferOutput and ByteBufferInput provide slightly worse performance, but this may be acceptable if the final destination of the bytes must be a ByteBuffer.
Variable length encoding is slower than fixed values, especially when there is a lot of data using it.
Chunked encoding uses an intermediary buffer so it adds one additional copy of all the bytes. This alone may be acceptable, however when used in a reentrant serializer, the serializer must create an OutputChunked or InputChunked for each object. Allocating and garbage collecting those buffers during serialization can have a negative impact on performance.
Reading and writing objects
Kryo has three sets of methods for reading and writing objects. If the concrete class of the object is not known and the object could be null:
kryo.writeClassAndObject(output, object);
Object object = kryo.readClassAndObject(input);
if (object instanceof SomeClass) {
// …
}
If the class is known and the object could be null:
kryo.writeObjectOrNull(output, object);
SomeClass object = kryo.readObjectOrNull(input, SomeClass.class);
If the class is known and the object cannot be null:
kryo.writeObject(output, object);
SomeClass object = kryo.readObject(input, SomeClass.class);
All of these methods first find the appropriate serializer to use, then use that to serialize or deserialize the object. Serializers can call these methods for recursive serialization. Multiple references to the same object and circular references are handled by Kryo automatically.
Besides methods to read and write objects, the Kryo class provides a way to register serializers, reads and writes class identifiers efficiently, handles null objects for serializers that can't accept nulls, and handles reading and writing object references (if enabled). This allows serializers to focus on their serialization tasks.
Round trip
While testing and exploring Kryo APIs, it can be useful to write an object to bytes, then read those bytes back to an object.
Kryo kryo = new Kryo();
// Register all classes to be serialized.
kryo.register(SomeClass.class);
SomeClass object1 = new SomeClass();
Output output = new Output(1024, -1);
kryo.writeObject(output, object1);
Input input = new Input(output.getBuffer(), 0, output.position());
SomeClass object2 = kryo.readObject(input, SomeClass.class);
In this example the Output starts with a buffer that has a capacity of 1024 bytes. If more bytes are written to the Output, the buffer will grow in size without limit. The Output does not need to be closed because it has not been given an OutputStream. The Input reads directly from the Output's
byte[]
buffer.Deep and shallow copies
Kryo supports making deep and shallow copies of objects using direct assignment from one object to another. This is more efficient than serializing to bytes and back to objects.
Kryo kryo = new Kryo();
SomeClass object = …
SomeClass copy1 = kryo.copy(object);
SomeClass copy2 = kryo.copyShallow(object);
All the serializers being used need to support copying. All serializers provided with Kryo support copying.
Like with serialization, when copying, multiple references to the same object and circular references are handled by Kryo automatically if references are enabled.
If using Kryo only for copying, registration can be safely disabled.
Kryo
getOriginalToCopyMap
can be used after an object graph is copied to obtain a map of old to new objects. The map is cleared automatically by Kryoreset
, so is only useful when KryosetAutoReset
is false.References
By default references are not enabled. This means if an object appears in an object graph multiple times, it will be written multiple times and will be deserialized as multiple, different objects. When references are disabled, circular references will cause serialization to fail. References are enabled or disabled with Kryo
setReferences
for serialization andsetCopyReferences
for copying.When references are enabled, a varint is written before each object the first time it appears in the object graph. For subsequent appearances of that class within the same object graph, only a varint is written. After deserialization the object references are restored, including any circular references. The serializers in use must support references by calling Kryo
reference
in Serializerread
.Enabling references impacts performance because every object that is read or written needs to be tracked.
ReferenceResolver
Under the covers, a ReferenceResolver handles tracking objects that have been read or written and provides int reference IDs. Multiple implementations are provided:
- MapReferenceResolver is used by default if a reference resolver is not specified. It uses Kryo's IdentityObjectIntMap (a cuckoo hashmap) to track written objects. This kind of map has very fast gets and does not allocate for put, but puts for very large numbers of objects can be somewhat slow.
- HashMapReferenceResolver uses a HashMap to track written objects. This kind of map allocates for put but may provide better performance for object graphs with a very high number of objects.
- ListReferenceResolver uses an ArrayList to track written objects. For object graphs with relatively few objects, this can be faster than using a map (~15% faster in some tests). This should not be used for graphs with many objects because it has a linear look up to find objects that have already been written.
ReferenceResolver
useReferences(Class)
can be overridden. It returns a boolean to decide if references are supported for a class. If a class doesn't support references, the varint reference ID is not written before objects of that type. If a class does not need references and objects of that type appear in the object graph many times, the serialized size can be greatly reduced by disabling references for that class. The default reference resolver returns false for all primitive wrappers and enums. It is common to also return false for String and other classes, depending on the object graphs being serialized.public boolean useReferences (Class type) {
return !Util.isWrapperClass(type) !Util.isEnum(type) type != String.class;
}
Reference limits
The reference resolver determines the maximum number of references in a single object graph. Java array indices are limited to
Integer.MAX_VALUE
, so reference resolvers that use data structures based on arrays may result in ajava.lang.NegativeArraySizeException
when serializing more than ~2 billion objects. Kryo uses int class IDs, so the maximum number of references in a single object graph is limited to the full range of positive and negative numbers in an int (~4 billion).Context
Kryo
getContext
returns a map for storing user data. The Kryo instance is available to all serializers, so this data is easily accessible to all serializers.Kryo
getGraphContext
is similar, but is cleared after each object graph is serialized or deserialized. This makes it easy to manage state that is only relevant for the current object graph. For example, this can be used to write some schema data the first time a class is encountered in an object graph. See CompatibleFieldSerializer for an example.Reset
By default, Kryo
reset
is called after each entire object graph is serialized. This resets unregistered class names in the class resolver, references to previously serialized or deserialized objects in the reference resolver, and clears the graph context. KryosetAutoReset(false)
can be used to disable callingreset
automatically, allowing that state to span multiple object graphs.Serializer framework
Kryo is a framework to facilitate serialization. The framework itself doesn't enforce a schema or care what or how data is written or read. Serializers are pluggable and make the decisions about what to read and write. Many serializers are provided out of the box to read and write data in various ways. While the provided serializers can read and write most objects, they can easily be replaced partially or completely with your own serializers.
Registration
When Kryo goes to write an instance of an object, first it may need to write something that identifies the object's class. By default, all classes that Kryo will read or write must be registered beforehand. Registration provides an int class ID, the serializer to use for the class, and the object instantiator used to create instances of the class.
Kryo kryo = new Kryo();
kryo.register(SomeClass.class);
Output output = …
SomeClass object = …
kryo.writeObject(output, object);
During deserialization, the registered classes must have the exact same IDs they had during serialization. When registered, a class is assigned the next available, lowest integer ID, which means the order classes are registered is important. The class ID can optionally be specified explicitly to make order unimportant:
Kryo kryo = new Kryo();
kryo.register(SomeClass.class, 9);
kryo.register(AnotherClass.class, 10);
kryo.register(YetAnotherClass.class, 11);
Class IDs -1 and -2 are reserved. Class IDs 0-8 are used by default for primitive types and String, though these IDs can be repurposed. The IDs are written as positive optimized varints, so are most efficient when they are small, positive integers. Negative IDs are not serialized efficiently.
ClassResolver
Under the covers, a ClassResolver handles actually reading and writing bytes to represent a class. The default implementation is sufficient in most cases, but it can be replaced to customize what happens when a class is registered, what an unregistered class is encountered during serialization, and what is read and written to represent a class.
Optional registration
Kryo can be configured to allow serialization without registering classes up front.
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
Output output = …
SomeClass object = …
kryo.writeObject(output, object);
Use of registered and unregistered classes can be mixed. Unregistered classes have two major drawbacks:
- There are security implications because it allows deserialization to create instances of any class. Classes with side effects during construction or finalization could be used for malicious purposes.
- Instead of writing a varint class ID (often 1-2 bytes), the fully qualified class name is written the first time an unregistered class appears in the object graph. Subsequent appearances of that class within the same object graph are written using a varint. Short package names could be considered to reduce the serialized size.
If using Kryo only for copying, registration can be safely disabled.
When registration is not required, Kryo
setWarnUnregisteredClasses
can be enabled to log a message when an unregistered class is encountered. This can be used to easily obtain a list of all unregistered classes. KryounregisteredClassMessage
can be overridden to customize the log message or take other actions.Default serializers
When a class is registered, a serializer instance can optionally be specified. During deserialization, the registered classes must have the exact same serializers and serializer configurations they had during serialization.
Kryo kryo = new Kryo();
kryo.register(SomeClass.class, new SomeSerializer());
kryo.register(AnotherClass.class, new AnotherSerializer());
If a serializer is not specified or when an unregistered class is encountered, a serializer is chosen automatically from a list of "default serializers" that maps a class to a serializer. Having many default serializers doesn't affect serialization performance, so by default Kryo has 50+ default serializers for various JRE classes. Additional default serializers can be added:
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.addDefaultSerializer(SomeClass.class, SomeSerializer.class);
Output output = …
SomeClass object = …
kryo.writeObject(output, object);
This will cause a SomeSerializer instance to be created when SomeClass or any class which extends or implements SomeClass is registered.
Default serializers are sorted so more specific classes are matched first, but are otherwise matched in the order they are added. The order they are added can be relevant for interfaces.
If no default serializers match a class, then the global default serializer is used. The global default serializer is set to FieldSerializer by default, but can be changed. Usually the global serializer is one that can handle many different types.
Kryo kryo = new Kryo();
kryo.setDefaultSerializer(TaggedFieldSerializer.class);
kryo.register(SomeClass.class);
With this code, assuming no default serializers match SomeClass, TaggedFieldSerializer will be used.
A class can also use the DefaultSerializer annotation, which will be used instead of choosing one of Kryo's default serializers:
@DefaultSerializer(SomeClassSerializer.class)
public class SomeClass {
// …
}
For maximum flexibility, Kryo
getDefaultSerializer
can be overridden to implement custom logic for choosing and instantiating a serializer.Serializer factories
The
addDefaultSerializer(Class, Class)
method does not allow for configuration of the serializer. A serializer factory can be set instead of a serializer class, allowing the factory to create and configure each serializer instance. Factories are provided for common serializers, often with agetConfig
method to configure the serializers that are created.Kryo kryo = new Kryo();
TaggedFieldSerializerFactory defaultFactory = new TaggedFieldSerializerFactory();
defaultFactory.getConfig().setReadUnknownTagData(true);
kryo.setDefaultSerializer(defaultFactory);
FieldSerializerFactory someClassFactory = new FieldSerializerFactory();
someClassFactory.getConfig().setFieldsCanBeNull(false);
kryo.register(SomeClass.class, someClassFactory);
The serializer factory has an
isSupported(Class)
method which allows it to decline to handle a class, even if it otherwise matches the class. This allows a factory to check for multiple interfaces or implement other logic.Object creation
While some serializers are for a specific class, others can serialize many different classes. Serializers can use Kryo
newInstance(Class)
to create an instance of any class. This is done by looking up the registration for the class, then using the registration's ObjectInstantiator. The instantiator can be specified on the registration.Registration registration = kryo.register(SomeClass.class);
registration.setInstantiator(new ObjectInstantiatorSomeClass() {
public SomeClass newInstance () {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(<span class="pl-s"><span class="pl-pds">"</span>some constructor arguments<span class="pl-pds">"</span></span>, <span class="pl-c1">1234</span>);
}
});
If the registration doesn't have an instantiator, one is provided by Kryo
newInstantiator
. To customize how objects are created, KryonewInstantiator
can be overridden or an InstantiatorStrategy provided.InstantiatorStrategy
Kryo provides DefaultInstantiatorStrategy which creates objects using ReflectASM to call a zero argument constructor. If that is not possible, it uses reflection to call a zero argument constructor. If that also fails, then it either throws an exception or tries a fallback InstantiatorStrategy. Reflection uses
setAccessible
, so a private zero argument constructor can be a good way to allow Kryo to create instances of a class without affecting the public API.DefaultInstantiatorStrategy is the recommended way of creating objects with Kryo. It runs constructors just like would be done with Java code. Alternative, extralinguistic mechanisms can also be used to create objects. The Objenesis StdInstantiatorStrategy uses JVM specific APIs to create an instance of a class without calling any constructor at all. Using this is dangerous because most classes expect their constructors to be called. Creating the object by bypassing its constructors may leave the object in an uninitialized or invalid state. Classes must be designed to be created in this way.
Kryo can be configured to try DefaultInstantiatorStrategy first, then fallback to StdInstantiatorStrategy if necessary.
kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
Another option is SerializingInstantiatorStrategy, which uses Java's built-in serialization mechanism to create an instance. Using this, the class must implement java.io.Serializable and the first zero argument constructor in a super class is invoked. This also bypasses constructors and so is dangerous for the same reasons as StdInstantiatorStrategy.
kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new SerializingInstantiatorStrategy()));
Overriding create
Alternatively, some generic serializers provide methods that can be overridden to customize object creation for a specific type, instead of calling Kryo
newInstance
.kryo.register(SomeClass.class, new FieldSerializer(kryo, SomeClass.class) {
protected T create (Kryo kryo, Input input, Class? extends T type) {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(<span class="pl-s"><span class="pl-pds">"</span>some constructor arguments<span class="pl-pds">"</span></span>, <span class="pl-c1">1234</span>);
}
});
Some serializers provide a
writeHeader
method that can be overridden to write data that is needed increate
at the right time.static public class TreeMapSerializer extends MapSerializerTreeMap {
protected void writeHeader (Kryo kryo, Output output, TreeMap map) {
kryo<span class="pl-k">.</span>writeClassAndObject(output, map<span class="pl-k">.</span>comparator());
}
protected TreeMap create (Kryo kryo, Input input, Class? extends TreeMap type, int size) {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">TreeMap</span>((<span class="pl-smi">Comparator</span>)kryo<span class="pl-k">.</span>readClassAndObject(input));
}
}
If a serializer doesn't provide
writeHeader
, writing data forcreate
can be done inwrite
.static public class SomeClassSerializer extends FieldSerializerSomeClass {
public SomeClassSerializer (Kryo kryo) {
<span class="pl-c1">super</span>(kryo, <span class="pl-smi">SomeClass</span><span class="pl-k">.</span>class);
}
public void write (Kryo kryo, Output output, SomeClass object) {
output<span class="pl-k">.</span>writeInt(object<span class="pl-k">.</span>value);
}
protected SomeClass create (Kryo kryo, Input input, Class? extends SomeClass type) {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(input<span class="pl-k">.</span>readInt());
}
}
Final classes
Even when a serializer knows the expected class for a value (eg a field's class), if the value's concrete class is not final then the serializer needs to first write the class ID, then the value. Final classes can be serialized more efficiently because they are non-polymorphic.
Kryo
isFinal
is used to determine if a class is final. This method can be overridden to return true even for types which are not final. For example, if an application uses ArrayList extensively but never uses an ArrayList subclass, treating ArrayList as final could allow FieldSerializer to save 1-2 bytes per ArrayList field.Closures
Kryo can serialize Java 8+ closures that implement java.io.Serializable, with some caveats. Closures serialized on one JVM may fail to be deserialized on a different JVM.
Kryo
isClosure
is used to determine if a class is a closure. If so, then ClosureSerializer.Closure is used to find the class registration instead of the closure's class. To serialize closures, the following classes must be registered: ClosureSerializer.Closure, SerializedLambda, Object[], and Class. Additionally, the closure's capturing class must be registered.kryo.register(Object[].class);
kryo.register(Class.class);
kryo.register(SerializedLambda.class);
kryo.register(ClosureSerializer.Closure.class, new ClosureSerializer());
kryo.register(CapturingClass.class);
CallableInteger closure1 = (CallableInteger java.io.Serializable)( () - 72363 );
Output output = new Output(1024, -1);
kryo.writeObject(output, closure1);
Input input = new Input(output.getBuffer(), 0, output.position());
CallableInteger closure2 = (CallableInteger)kryo.readObject(input, ClosureSerializer.Closure.class);
Serializing closures which do not implement Serializable is possible with some effort.
Compression and encryption
Kryo supports streams, so it is trivial to use compression or encryption on all of the serialized bytes:
OutputStream outputStream = new DeflaterOutputStream(new FileOutputStream("file.bin"));
Output output = new Output(outputStream);
Kryo kryo = new Kryo();
kryo.writeObject(output, object);
output.close();
If needed, a serializer can be used to compress or encrypt the bytes for only a subset of the bytes for an object graph. For example, see DeflateSerializer or BlowfishSerializer. These serializers wrap another serializer to encode and decode the bytes.
Implementing a serializer
The Serializer abstract class defines methods to go from objects to bytes and bytes to objects.
public class ColorSerializer extends SerializerColor {
public void write (Kryo kryo, Output output, Color color) {
output<span class="pl-k">.</span>writeInt(color<span class="pl-k">.</span>getRGB());
}
public Color read (Kryo kryo, Input input, Class? extends Color type) {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">Color</span>(input<span class="pl-k">.</span>readInt());
}
}
Serializer has only two methods that must be implemented.
write
writes the object as bytes to the Output.read
creates a new instance of the object and reads from the Input to populate it.Serializer references
When Kryo is used to read a nested object in Serializer
read
then Kryoreference
must first be called with the parent object if it is possible for the nested object to reference the parent object. It is unnecessary to call Kryoreference
if the nested objects can't possibly reference the parent object, if Kryo is not being used for nested objects, or if references are not being used. If nested objects can use the same serializer, the serializer must be reentrant.public SomeClass read (Kryo kryo, Input input, Class? extends SomeClass type) {
SomeClass object = new SomeClass();
kryo.reference(object);
// Read objects that may reference the SomeClass instance.
object.someField = kryo.readClassAndObject(input);
return object;
}
Nested serializers
Serializers should not usually make direct use of other serializers, instead the Kryo read and write methods should be used. This allows Kryo to orchestrate serialization and handle features such as references and null objects. Sometimes a serializer knows which serializer to use for a nested object. In that case, it should use Kryo's read and write methods which accept a serializer.
If the object could be null:
Serializer serializer = ...
kryo.writeObjectOrNull(output, object, serializer);
SomeClass object = kryo.readObjectOrNull(input, SomeClass.class, serializer);
If the object cannot be null:
Serializer serializer = ...
kryo.writeObject(output, object, serializer);
SomeClass object = kryo.readObject(input, SomeClass.class, serializer);
During serialization Kryo
getDepth
provides the current depth of the object graph.KryoException
When a serialization fails, a KryoException can be thrown with serialization trace information about where in the object graph the exception occurred. When using nested serializers, KryoException can be caught to add serialization trace information.
Object object = ...
Field[] fields = …
for (Field field : fields) {
try {
<span class="pl-c"><span class="pl-c">//</span> Use other serializers to serialize each field.</span>
} catch (KryoException ex) {
ex<span class="pl-k">.</span>addTrace(field<span class="pl-k">.</span>getName() <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span> (<span class="pl-pds">"</span></span> <span class="pl-k">+</span> object<span class="pl-k">.</span>getClass()<span class="pl-k">.</span>getName() <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>)<span class="pl-pds">"</span></span>); <span class="pl-k">throw</span> ex;
} catch (Throwable t) {
<span class="pl-smi">KryoException</span> ex <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">KryoException</span>(t); ex<span class="pl-k">.</span>addTrace(field<span class="pl-k">.</span>getName() <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span> (<span class="pl-pds">"</span></span> <span class="pl-k">+</span> object<span class="pl-k">.</span>getClass()<span class="pl-k">.</span>getName() <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>)<span class="pl-pds">"</span></span>); <span class="pl-k">throw</span> ex;
}
}
Stack size
The serializers Kryo provides use the call stack when serializing nested objects. Kryo minimizes stack calls, but a stack overflow can occur for extremely deep object graphs. This is a common issue for most serialization libraries, including the built-in Java serialization. The stack size can be increased using
-Xss
, but note that this applies to all threads. Large stack sizes in a JVM with many threads may use a large amount of memory.Kryo
setMaxDepth
can be used to limit the maximum depth of an object graph. This can prevent malicious data from causing a stack overflow.Accepting null
By default, serializers will never receive a null, instead Kryo will write a byte as needed to denote null or not null. If a serializer can be more efficient by handling nulls itself, it can call Serializer
setAcceptsNull(true)
. This can also be used to avoid writing the null denoting byte when it is known that all instances the serializer will handle will never be null.Generics
Kryo
getGenerics
provides generic type information so serializers can be more efficient. This is most commonly used to avoid writing the class when the type parameter class is final.If the class has a single type parameter,
nextGenericClass
returns the type parameter class, or null if none. After reading or writing any nested objects,popGenericType
must be called. See CollectionSerializer for an example.public class SomeClassT {
public T value;
}
public class SomeClassSerializer extends SerializerSomeClass {
public void write (Kryo kryo, Output output, SomeClass object) {
<span class="pl-smi">Class</span> valueClass <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>nextGenericClass(); <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); kryo<span class="pl-k">.</span>writeObjectOrNull(output, object<span class="pl-k">.</span>value, serializer); } <span class="pl-k">else</span> kryo<span class="pl-k">.</span>writeClassAndObject(output, object<span class="pl-k">.</span>value); kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>popGenericType();
}
public SomeClass read (Kryo kryo, Input input, Class? extends SomeClass type) {
<span class="pl-smi">Class</span> valueClass <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>nextGenericClass(); <span class="pl-smi">SomeClass</span> object <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); kryo<span class="pl-k">.</span>reference(object); <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readObjectOrNull(input, valueClass, serializer); } <span class="pl-k">else</span> object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readClassAndObject(input); kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>popGenericType(); <span class="pl-k">return</span> object;
}
}
For a class with multiple type parameters,
nextGenericTypes
returns an array of GenericType instances andresolve
is used to obtain the class for each GenericType. After reading or writing any nested objects,popGenericType
must be called. See MapSerializer for an example.public class SomeClassK, V {
public K key;
public V value;
}
public class SomeClassSerializer extends SerializerSomeClass {
public void write (Kryo kryo, Output output, SomeClass object) {
<span class="pl-smi">Class</span> keyClass <span class="pl-k">=</span> <span class="pl-c1">null</span>, valueClass <span class="pl-k">=</span> <span class="pl-c1">null</span>; <span class="pl-k">GenericType</span>[] genericTypes <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>nextGenericTypes(); <span class="pl-k">if</span> (genericTypes <span class="pl-k">!=</span> <span class="pl-c1">null</span>) { keyClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">0</span>]<span class="pl-k">.</span>resolve(kryo<span class="pl-k">.</span>getGenerics()); valueClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">1</span>]<span class="pl-k">.</span>resolve(kryo<span class="pl-k">.</span>getGenerics()); } <span class="pl-k">if</span> (keyClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(keyClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(keyClass); kryo<span class="pl-k">.</span>writeObjectOrNull(output, object<span class="pl-k">.</span>key, serializer); } <span class="pl-k">else</span> kryo<span class="pl-k">.</span>writeClassAndObject(output, object<span class="pl-k">.</span>key); <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); kryo<span class="pl-k">.</span>writeObjectOrNull(output, object<span class="pl-k">.</span>value, serializer); } <span class="pl-k">else</span> kryo<span class="pl-k">.</span>writeClassAndObject(output, object<span class="pl-k">.</span>value); kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>popGenericType();
}
public SomeClass read (Kryo kryo, Input input, Class? extends SomeClass type) {
<span class="pl-smi">Class</span> keyClass <span class="pl-k">=</span> <span class="pl-c1">null</span>, valueClass <span class="pl-k">=</span> <span class="pl-c1">null</span>; <span class="pl-k">GenericType</span>[] genericTypes <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>nextGenericTypes(); <span class="pl-k">if</span> (genericTypes <span class="pl-k">!=</span> <span class="pl-c1">null</span>) { keyClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">0</span>]<span class="pl-k">.</span>resolve(kryo<span class="pl-k">.</span>getGenerics()); valueClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">1</span>]<span class="pl-k">.</span>resolve(kryo<span class="pl-k">.</span>getGenerics()); } <span class="pl-smi">SomeClass</span> object <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); kryo<span class="pl-k">.</span>reference(object); <span class="pl-k">if</span> (keyClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(keyClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(keyClass); object<span class="pl-k">.</span>key <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readObjectOrNull(input, keyClass, serializer); } <span class="pl-k">else</span> object<span class="pl-k">.</span>key <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readClassAndObject(input); <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readObjectOrNull(input, valueClass, serializer); } <span class="pl-k">else</span> object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readClassAndObject(input); kryo<span class="pl-k">.</span>getGenerics()<span class="pl-k">.</span>popGenericType(); <span class="pl-k">return</span> object;
}
}
For serializers which pass type parameter information for nested objects in the object graph (somewhat advanced usage), first GenericsHierarchy is used to store the type parameters for a class. During serialization, Generics
pushTypeVariables
is called before generic types are resolved (if any). If 0 is returned, this must be followed by GenericspopTypeVariables
. See FieldSerializer for an example.public class SomeClassT {
T value;
ListT list;
}
public class SomeClassSerializer extends SerializerSomeClass {
private final GenericsHierarchy genericsHierarchy;
public SomeClassSerializer () {
genericsHierarchy <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">GenericsHierarchy</span>(<span class="pl-smi">SomeClass</span><span class="pl-k">.</span>class);
}
public void write (Kryo kryo, Output output, SomeClass object) {
<span class="pl-smi">Class</span> valueClass <span class="pl-k">=</span> <span class="pl-c1">null</span>; <span class="pl-smi">Generics</span> generics <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics(); <span class="pl-k">int</span> pop <span class="pl-k">=</span> <span class="pl-c1">0</span>; <span class="pl-k">GenericType</span>[] genericTypes <span class="pl-k">=</span> generics<span class="pl-k">.</span>nextGenericTypes(); <span class="pl-k">if</span> (genericTypes <span class="pl-k">!=</span> <span class="pl-c1">null</span>) { pop <span class="pl-k">=</span> generics<span class="pl-k">.</span>pushTypeVariables(genericsHierarchy, genericTypes); valueClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">0</span>]<span class="pl-k">.</span>resolve(generics); } <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); kryo<span class="pl-k">.</span>writeObjectOrNull(output, object<span class="pl-k">.</span>value, serializer); } <span class="pl-k">else</span> kryo<span class="pl-k">.</span>writeClassAndObject(output, object<span class="pl-k">.</span>value); kryo<span class="pl-k">.</span>writeClassAndObject(output, object<span class="pl-k">.</span>list); <span class="pl-k">if</span> (pop <span class="pl-k">></span> <span class="pl-c1">0</span>) generics<span class="pl-k">.</span>popTypeVariables(pop); generics<span class="pl-k">.</span>popGenericType();
}
public SomeClass read (Kryo kryo, Input input, Class? extends SomeClass type) {
<span class="pl-smi">Class</span> valueClass <span class="pl-k">=</span> <span class="pl-c1">null</span>; <span class="pl-smi">Generics</span> generics <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getGenerics(); <span class="pl-k">int</span> pop <span class="pl-k">=</span> <span class="pl-c1">0</span>; <span class="pl-k">GenericType</span>[] genericTypes <span class="pl-k">=</span> generics<span class="pl-k">.</span>nextGenericTypes(); <span class="pl-k">if</span> (genericTypes <span class="pl-k">!=</span> <span class="pl-c1">null</span>) { pop <span class="pl-k">=</span> generics<span class="pl-k">.</span>pushTypeVariables(genericsHierarchy, genericTypes); valueClass <span class="pl-k">=</span> genericTypes[<span class="pl-c1">0</span>]<span class="pl-k">.</span>resolve(generics); } <span class="pl-smi">SomeClass</span> object <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); kryo<span class="pl-k">.</span>reference(object); <span class="pl-k">if</span> (valueClass <span class="pl-k">!=</span> <span class="pl-c1">null</span> <span class="pl-k">&&</span> kryo<span class="pl-k">.</span>isFinal(valueClass)) { <span class="pl-smi">Serializer</span> serializer <span class="pl-k">=</span> kryo<span class="pl-k">.</span>getSerializer(valueClass); object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readObjectOrNull(input, valueClass, serializer); } <span class="pl-k">else</span> object<span class="pl-k">.</span>value <span class="pl-k">=</span> kryo<span class="pl-k">.</span>readClassAndObject(input); object<span class="pl-k">.</span>list <span class="pl-k">=</span> (<span class="pl-smi">List</span>)kryo<span class="pl-k">.</span>readClassAndObject(input); <span class="pl-k">if</span> (pop <span class="pl-k">></span> <span class="pl-c1">0</span>) generics<span class="pl-k">.</span>popTypeVariables(pop); generics<span class="pl-k">.</span>popGenericType(); <span class="pl-k">return</span> object;
}
}
KryoSerializable
Instead of using a serializer, a class can choose to do its own serialization by implementing KryoSerializable (similar to java.io.Externalizable).
public class SomeClass implements KryoSerializable {
private int value;
public void write (Kryo kryo, Output output) {
output<span class="pl-k">.</span>writeInt(value, <span class="pl-c1">false</span>);
}
public void read (Kryo kryo, Input input) {
value <span class="pl-k">=</span> input<span class="pl-k">.</span>readInt(<span class="pl-c1">false</span>);
}
}
Obviously the instance must already be created before
read
can be called, so the class isn't able to control its own creation. A KryoSerializable class will use the default serializer KryoSerializableSerializer, which uses KryonewInstance
to create a new instance. It is trivial to write your own serializer to customize the process, call methods before or after serialiation, etc.Serializer copying
Serializers only support copying if
copy
is overridden. Similar to Serializerread
, this method contains the logic to create and configure the copy. Just likeread
, Kryoreference
must be called before Kryo is used to copy child objects, if any of the child objects could reference the parent object.class SomeClassSerializer extends SerializerSomeClass {
public SomeClass copy (Kryo kryo, SomeClass original) {
<span class="pl-smi">SomeClass</span> copy <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); kryo<span class="pl-k">.</span>reference(copy); copy<span class="pl-k">.</span>intValue <span class="pl-k">=</span> original<span class="pl-k">.</span>intValue; copy<span class="pl-k">.</span>object <span class="pl-k">=</span> kryo<span class="pl-k">.</span>copy(original<span class="pl-k">.</span>object); <span class="pl-k">return</span> copy;
}
}
KryoCopyable
Instead of using a serializer, classes can implement KryoCopyable to do their own copying:
public class SomeClass implements KryoCopyableSomeClass {
public SomeClass copy (Kryo kryo) {
<span class="pl-smi">SomeClass</span> copy <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">SomeClass</span>(); kryo<span class="pl-k">.</span>reference(copy); copy<span class="pl-k">.</span>intValue <span class="pl-k">=</span> intValue; copy<span class="pl-k">.</span>object <span class="pl-k">=</span> kryo<span class="pl-k">.</span>copy(object); <span class="pl-k">return</span> copy;
}
}
Immutable serializers
Serializer
setImmutable(true)
can be used when the type is immutable. In that case, Serializercopy
does not need to be implemented -- the defaultcopy
implementation will return the original object.Kryo versioning and upgrading
The following rules of thumb are applied to Kryo's version numbering:
- The major version is increased if serialization compatibility is broken. This means data serialized with a previous version may not be deserialized with the new version.
- The minor version is increased if binary or source compatibility of the documented public API is broken. To avoid increasing the version when very few users are affected, some minor breakage is allowed if it occurs in public classes that are seldom used or not intended for general usage.
Upgrading any dependency is a significant event, but a serialization library is more prone to breakage than most dependencies. When upgrading Kryo check the version differences and test the new version thoroughly in your own applications. We try to make it as safe and easy as possible.
- At development time serialization compatibility is tested for the different binary formats and default serializers.
- At development time binary and source compatibility is tracked with clirr.
- For each release a changelog is provided that also contains a section reporting the serialization, binary, and source compatibilities.
- For reporting binary and source compatibility japi-compliance-checker is used.
Interoperability
The Kryo serializers provided by default assume that Java will be used for deserialization, so they do not explicitly define the format that is written. Serializers could be written using a standardized format that is more easily read by other languages, but this is not provided by default.
Compatibility
For some needs, such as long term storage of serialized bytes, it can be important how serialization handles changes to classes. This is known as forward compatibility (reading bytes serialized by newer classes) and backward compatibility (reading bytes serialized by older classes). Kryo provides a few generic serializers which take different approaches to handling compatibility. Additional serializers can easily be developed for forward and backward compatibility, such as a serializer that uses an external, hand written schema.
Serializers
Kryo provides many serializers with various configuration options and levels of compatibility. Additional serializers can be found in the kryo-serializers sister project, which hosts serializers that access private APIs or are otherwise not perfectly safe on all JVMs. More serializers can be found in the links section.
FieldSerializer
FieldSerializer works by serializing each non-transient field. It can serialize POJOs and many other classes without any configuration. All non-public fields are written and read by default, so it is important to evaluate each class that will be serialized. If fields are public, serialization may be faster.
FieldSerializer is efficient by writing only the field data, without any schema information, using the Java class files as the schema. It does not support adding, removing, or changing the type of fields without invalidating previously serialized bytes. Renaming fields is allowed only if it doesn't change the alphabetical order of the fields.
FieldSerializer's compatibility drawbacks can be acceptable in many situations, such as when sending data over a network, but may not be a good choice for long term data storage because the Java classes cannot evolve.
FieldSerializer settings
Setting Description Default value
fieldsCanBeNull
When false it is assumed that no field values are null, which can save 0-1 byte per field. truesetFieldsAsAccessible
When true, all non-transient fields (inlcuding private fields) will be serialized andsetAccessible
if necessary. If false, only fields in the public API will be serialized. trueignoreSyntheticFields
If true, synthetic fields (generated by the compiler for scoping) are serialized. falsefixedFieldTypes
If true, it is assumed every field value's concrete type matches the field's type. This removes the need to write the class ID for field values. falsecopyTransient
If true, all transient fields will be copied. trueserializeTransient
If true, transient fields will be serialized. falsevariableLengthEncoding
If true, variable length values are used for int and long fields. trueextendedFieldNames
If true, field names are prefixed by their declaring class. This can avoid conflicts when a subclass has a field with the same name as a super class. falseCachedField settings
FieldSerializer provides the fields that will be serialized. Fields can be removed, so they won't be serialized. Fields can be configured to make serialiation more efficient.
FieldSerializer fieldSerializer = ...
fieldSerializer.removeField(“id”); // Won’t be serialized.
CachedField nameField = fieldSerializer.getField(“name”);
nameField.setCanBeNull(false);
CachedField someClassField = fieldSerializer.getField(“someClass”);
someClassField.setClass(SomeClass.class, new SomeClassSerializer());
Setting Description Default value
canBeNull
When false it is assumed the field value is never null, which can save 0-1 byte. truevalueClass
Sets the concrete class and serializer to use for the field value. This removes the need to write the class ID for the value. If the field value's class is a primitive, primitive wrapper, or final, this setting defaults to the field's class. nullserializer
Sets the serializer to use for the field value. If the serializer is set, some serializers required the value class to also be set. If null, the serializer registered with Kryo for the field value's class will be used. nullvariableLengthEncoding
If true, variable length values are used. This only applies to int or long fields. trueoptimizePositive
If true, positive values are optimized for variable length values. This only applies to int or long fields when variable length encoding is used. trueFieldSerializer annotations
Annotations can be used to configure the serializers for each field.
Annotation Description
@Bind
Sets the CachedField settings for any field.@CollectionBind
Sets the CollectionSerializer settings for Collection fields.@MapBind
Sets the MapSerializer settings for Map fields.@NotNull
Marks a field as never being null.public class SomeClass {
@NotNull
@Bind(serializer = StringSerializer.class, valueClass = String.class, canBeNull = false)
Object stringField;
@Bind(variableLengthEncoding = false)
int intField;
@BindMap(
<span class="pl-c1">keySerializer</span> <span class="pl-k">=</span> <span class="pl-smi">StringSerializer</span><span class="pl-k">.</span>class, <span class="pl-c1">valueSerializer</span> <span class="pl-k">=</span> <span class="pl-smi">IntArraySerializer</span><span class="pl-k">.</span>class, <span class="pl-c1">keyClass</span> <span class="pl-k">=</span> <span class="pl-smi">String</span><span class="pl-k">.</span>class, <span class="pl-c1">valueClass</span> <span class="pl-k">=</span> <span class="pl-k">int</span>[]<span class="pl-k">.</span>class, <span class="pl-c1">keysCanBeNull</span> <span class="pl-k">=</span> <span class="pl-c1">false</span>)
Map map;
@BindCollection(
<span class="pl-c1">elementSerializer</span> <span class="pl-k">=</span> <span class="pl-smi">LongArraySerializer</span><span class="pl-k">.</span>class, <span class="pl-c1">elementClass</span> <span class="pl-k">=</span> <span class="pl-k">long</span>[]<span class="pl-k">.</span>class, <span class="pl-c1">elementsCanBeNull</span> <span class="pl-k">=</span> <span class="pl-c1">false</span>)
Collection collection;
}
VersionFieldSerializer
VersionFieldSerializer extends FieldSerializer and provides backward compatibility. This means fields can be added without invalidating previously serialized bytes. Removing, renaming, or changing the type of a field is not supported.
When a field is added, it must have the
@Since(int)
annotation to indicate the version it was added in order to be compatible with previously serialized bytes. The annotation value must never change.VersionFieldSerializer adds very little overhead to FieldSerializer: a single additional varint.
VersionFieldSerializer settings
Setting Description Default value
compatible
When false, an exception is thrown when reading an object with a different version. The version of an object is the maximum version of any field. trueVersionFieldSerializer also inherits all the settings of FieldSerializer.
TaggedFieldSerializer
TaggedFieldSerializer extends FieldSerializer to provide backward compatibility and optional forward compatibility. This means fields can be added or renamed and optionally removed without invalidating previously serialized bytes. Changing the type of a field is not supported.
Only fields that have a
@Tag(int)
annotation are serialized. Field tag values must be unique, both within a class and all its super classes. An exception is thrown if duplicate tag values are encountered.The forward and backward compatibility and serialization performance depends on the
readUnknownTagData
andchunkedEncoding
settings. Additionally, a varint is written before each field for the tag value.When
readUnknownTagData
andchunkedEncoding
are false, fields must not be removed but the@Deprecated
annotation can be applied. Deprecated fields are read when reading old bytes but aren't written to new bytes. Classes can evolve by reading the values of deprecated fields and writing them elsewhere. Fields can be renamed and/or made private to reduce clutter in the class (eg,ignored1
,ignored2
).TaggedFieldSerializer settings
Setting Description Default value
readUnknownTagData
When false and an unknown tag is encountered, an exception is thrown or, ifchunkedEncoding
is true, the data is skipped.
When true, the class for each field value is written before the value. When an unknown tag is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, ifchunkedEncoding
is true, the data is skipped.
In either case, if the data is skipped and references are enabled, then any references in the skipped data are not read and further deserialization may receive the wrong references and fail.chunkedEncoding
When true, fields are written with chunked encoding to allow unknown field data to be skipped. This impacts performance. falsechunkSize
The maximum size of each chunk for chunked encoding. 1024TaggedFieldSerializer also inherits all the settings of FieldSerializer.
CompatibleFieldSerializer
CompatibleFieldSerializer extends FieldSerializer to provided both forward and backward compatibility. This means fields can be added or removed without invalidating previously serialized bytes. Renaming or changing the type of a field is not supported. Like FieldSerializer, it can serialize most classes without needing annotations.
The forward and backward compatibility and serialization performance depends on the
readUnknownFieldData
andchunkedEncoding
settings. Additionally, the first time the class is encountered in the serialized bytes, a simple schema is written containing the field name strings. Because field data is identified by name, if a super class has a field with the same name as a subclass,extendedFieldNames
must be true.CompatibleFieldSerializer settings
Setting Description Default value
readUnknownFieldData
When false and an unknown field is encountered, an exception is thrown or, ifchunkedEncoding
is true, the data is skipped.
When true, the class for each field value is written before the value. When an unknown field is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, ifchunkedEncoding
is true, the data is skipped.
In either case, if the data is skipped and references are enabled, then any references in the skipped data are not read and further deserialization may receive the wrong references and fail.chunkedEncoding
When true, fields are written with chunked encoding to allow unknown field data to be skipped. This impacts performance. falsechunkSize
The maximum size of each chunk for chunked encoding. 1024CompatibleFieldSerializer also inherits all the settings of FieldSerializer.
BeanSerializer
BeanSerializer is very similar to FieldSerializer, except it uses bean getter and setter methods rather than direct field access. This slightly slower, but may be safer because it uses the public API to configure the object. Like FieldSerializer, it provides no forward or backward compatibility.
CollectionSerializer
CollectionSerializer serializes objects that implement the java.util.Collection interface.
CollectionSerializer settings
Setting Description Default value
elementsCanBeNull
When false it is assumed that no elements in the collection are null, which can save 0-1 byte per element. trueelementClass
Sets the concrete class to use for each element in the collection. This removes the need to write the class ID for each element. If the element class is known (eg through generics) and a primitive, primitive wrapper, or final, then CollectionSerializer won't write the class ID even when this setting is null. nullelementSerializer
Sets the serializer to use for every element in the collection. If the serializer is set, some serializers required the value class to also be set. If null, the serializer registered with Kryo for each element's class will be used. nullMapSerializer
MapSerializer serializes objects that implement the java.util.Map interface.
MapSerializer settings
Setting Description Default value
keysCanBeNull
When false it is assumed that no keys in the map are null, which can save 0-1 byte per entry. truevaluesCanBeNull
When false it is assumed that no values in the map are null, which can save 0-1 byte per entry. truekeyClass
Sets the concrete class to use for every key in the map. This removes the need to write the class ID for each key. nullvalueClass
Sets the concrete class to use for every value in the map. This removes the need to write the class ID for each value. nullkeySerializer
Sets the serializer to use for every key in the map. If the value serializer is set, some serializers required the value class to also be set. If null, the serializer registered with Kryo for each key's class will be used. nullvalueSerializer
Sets the serializer to use for every value in the map. If the key serializer is set, some serializers required the value class to also be set. If null, the serializer registered with Kryo for each value's class will be used. nullJavaSerializer and ExternalizableSerializer
JavaSerializer and ExternalizableSerializer are Kryo serializers which uses Java's built-in serialization. This is as slow as usual Java serialization, but may be necessary for legacy classes.
java.io.Externalizable and java.io.Serializable do not have default serializers set by default, so the default serializers must be set manually or the serializers set when the class is registered.
class SomeClass implements Externalizable { / ... / }
kryo.addDefaultSerializer(Externalizable.class, ExternalizableSerializer.class);
kryo.register(SomeClass.class);
kryo.register(SomeClass.class, new JavaSerializer());
kryo.register(SomeClass.class, new ExternalizableSerializer());
Logging
Kryo makes use of the low overhead, lightweight MinLog logging library. The logging level can be set by one of the following methods:
Log.ERROR();
Log.WARN();
Log.INFO();
Log.DEBUG();
Log.TRACE();
Kryo does no logging at
INFO
(the default) and above levels.DEBUG
is convenient to use during development.TRACE
is good to use when debugging a specific problem, but generally outputs too much information to leave on.MinLog supports a fixed logging level, which causes the Java compiler to remove logging statements below that level at compile time. Kryo must be compiled with a fixed logging level MinLog JAR.
Thread safety
Kryo is not thread safe. Each thread should have its own Kryo, Input, and Output instances.
Pooling
Because Kryo is not thread safe and constructing and configuring a Kryo instance is relatively expensive, in a multithreaded environment ThreadLocal or pooling might be considered.
static private final ThreadLocalKryo kryos = new ThreadLocalKryo() {
protected Kryo initialValue() {
<span class="pl-smi">Kryo</span> kryo <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">Kryo</span>(); <span class="pl-c"><span class="pl-c">//</span> Configure the Kryo instance.</span> <span class="pl-k">return</span> kryo;
};
};
Kryo kryo = kryos.get();
For pooling, Kryo provides the Pool class which can pool Kryo, Input, Output, or instances of any other class.
// Pool constructor arguments: thread safe, soft references, maximum capacity
PoolKryo kryoPool = new PoolKryo(true, false, 8) {
protected Kryo create () {
<span class="pl-smi">Kryo</span> kryo <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-smi">Kryo</span>(); <span class="pl-c"><span class="pl-c">//</span> Configure the Kryo instance.</span> <span class="pl-k">return</span> kryo;
}
};
Kryo kryo = kryoPool.obtain();
// Use the Kryo instance here.
kryoPool.free(kryo);
PoolOutput outputPool = new PoolOutput(true, false, 16) {
protected Output create () {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">Output</span>(<span class="pl-c1">1024</span>, <span class="pl-k">-</span><span class="pl-c1">1</span>);
}
};
Output output = outputPool.obtain();
// Use the Output instance here.
outputPool.free(output);
PoolInput inputPool = new PoolInput(true, false, 16) {
protected Input create () {
<span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-smi">Input</span>(<span class="pl-c1">1024</span>, <span class="pl-k">-</span><span class="pl-c1">1</span>);
}
};
Input input = inputPool.obtain();
// Use the Input instance here.
inputPool.free(input);
If
true
is passed as the first argument to the Pool constructor, the Pool uses synchronization internally and can be accessed by multiple threads concurrently.If
true
is passed as the second argument to the Pool constructor, the Pool stores objects using java.lang.ref.SoftReference. This allows objects in the pool to be garbage collected when memory pressure on the JVM is high. Poolclean
removes all soft references whose object has been garbage collected. This can reduce the size of the pool when no maximum capacity has been set. When the pool has a maximum capacity, it is not necessary to callclean
because Poolfree
will try to remove an empty reference if the maximum capacity has been reached.The third Pool parameter is the maximum capacity. If an object is freed and the pool already contains the maximum number of free objects, the specified object is reset but not added to the pool. The maximum capacity may be omitted for no limit.
If an object implements Pool.Poolable then Poolable
reset
is called when the object is freed. This gives the object a chance to reset its state for reuse in the future. Alternatively, Poolreset
can be overridden to reset objects. Input and Output implement Poolable to set theirposition
andtotal
to 0. Kryo implements Poolable to reset its object graph state.Pool
getFree
returns the number of objects available to be obtained. If using soft references, this number may include objects that have been garbage collected.clean
may be used first to remove empty soft references.Pool
getPeak
returns the all-time highest number of free objects. This can help determine if a pool's maximum capacity is set appropriately. It can be reset any time withresetPeak
.Benchmarks
Kryo provides a number of JMH-based benchmarks and R/ggplot2 files.
Kryo can be compared to many other serialization libraries in the JVM Serializers project. The benchmarks are small, dated, and homegrown rather than using JMH, so are less trustworthy. Also, it is very difficult to thoroughly compare serialization libraries using a benchmark. Libraries have many different features and often have different goals, so they may excel at solving completely different problems. To understand these benchmarks, the code being run and data being serialized should be analyzed and contrasted with your specific needs. Some serializers are highly optimized and use pages of code, others use only a few lines. This is good to show what is possible, but may not be a relevant comparison for many situations.
Links
Projects using Kryo
There are a number of projects using Kryo. A few are listed below. Please submit a pull request if you'd like your project included here.
- KryoNet (NIO networking)
- kryo-serializers (additional serializers)
- Twitter's Scalding (Scala API for Cascading)
- Twitter's Chill (Kryo serializers for Scala)
- Apache Fluo (Kryo is default serialization for Fluo Recipes)
- Apache Hive (query plan serialization)
- Apache Spark (shuffled/cached data serialization)
- DataNucleus (JDO/JPA persistence framework)
- CloudPelican
- Yahoo's S4 (distributed stream computing)
- Storm (distributed realtime computation system, in turn used by many others)
- Cascalog (Clojure/Java data processing and querying details)
- memcached-session-manager (Tomcat high-availability sessions)
- Mobility-RPC (RPC enabling distributed applications)
- akka-kryo-serialization (Kryo serializers for Akka)
- Groupon
- Jive
- DestroyAllHumans (controls a robot!)
- Mybatis Redis-Cache (MyBatis Redis Cache adapter)
- Apache Dubbo (high performance, open source RPC framework)
Scala
- Twitter's Chill (Kryo serializers for Scala)
- akka-kryo-serialization (Kryo serializers for Scala and Akka)
- Twitter's Scalding (Scala API for Cascading)
- Kryo Serializers (Additional serializers for Java)
- Kryo Macros (Scala macros for compile-time generation of Kryo serializers)
Clojure
- Carbonite (Kryo serializers for Clojure)
Objective-C
- kryococoa (Objective-C port of Kryo)
</div>
<summary data-hotkey="l" aria-label="Jump to line"></summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast linejump" aria-label="Jump to line"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-jump-to-line-form Box-body d-flex" action="" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓" /> <input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line…" aria-label="Jump to line" autofocus> <button type="submit" class="btn" data-close-dialog>Go</button>
</main>
<ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0"> <li class="mr-3 mr-lg-0"> 2019 <span title="0.24981s from unicorn-57df4d69d-xhkvm">GitHub</span>, Inc.</li> <li class="mr-3 mr-lg-0">Terms</li> <li class="mr-3 mr-lg-0">Privacy</li> <li class="mr-3 mr-lg-0">Security</li> <li class="mr-3 mr-lg-0">Status</li> <li>Help</li> </ul> <a aria-label="Homepage" title="GitHub" class="footer-octicon d-none d-lg-block mx-lg-4" href="https://github.com"> <svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
<li class="mr-3 mr-lg-0">Contact GitHub</li> <li class="mr-3 mr-lg-0">Pricing</li> <li class="mr-3 mr-lg-0">API</li> <li class="mr-3 mr-lg-0">Training</li> <li class="mr-3 mr-lg-0">Blog</li> <li>About</li> </ul>
<span class="f6 text-gray-light"></span>
<script crossorigin="anonymous" integrity="sha512-wnzuXnJOv44OjFcu212AzXLSnG/ZC2w+604e2QGofkYgeMWZYQfHPb6NeH6194cJ8HvT0UEsmHzlCSXPVHcg6w==" type="application/javascript" src="https://github.githubassets.com/assets/frameworks-069938ed.js"></script> <script crossorigin="anonymous" async="async" integrity="sha512-/Y1wpl1aY/vedgylTJDuNAeobNLd980HlatSO7pdREx2e+kh4ijbkH0T8qp/+sJPD4FOBcodJ2AyDATrXVdVKA==" type="application/javascript" src="https://github.githubassets.com/assets/github-bootstrap-d62e19fe.js"></script>
<summary role="button" aria-label="Close dialog"></summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal"> <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog> <svg class="octicon octicon-x" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z"/></svg> </button> <div class="octocat-spinner my-6 js-details-dialog-spinner"></div> </details-dialog>