Mastering High Performance with Kotlin - Igor Kucherenko - E-Book

Mastering High Performance with Kotlin E-Book

Igor Kucherenko

0,0
35,99 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

Find out how to write Kotlin code without overhead and how to use different profiling tools and bytecode viewer to inspect expressions of Kotlin language.

Key Features

  • Apply modern Kotlin features to speed up processing and implement highly efficient and reliable codes.
  • Learn memory optimization, concurrency, multi-threading, scaling, and caching techniques to achieve high performance.
  • Learn how to prevent unnecessary overhead and use profiling tools to detect performance issues.

Book Description

The ease with which we write applications has been increasing, but with it comes the need to address their performance. A balancing act between easily implementing complex applications and keeping their performance optimal is a present-day requirement In this book, we explore how to achieve this crucial balance, while developing and deploying applications with Kotlin.

The book starts by analyzing various Kotlin specifcations to identify those that have a potentially adverse effect on performance. Then, we move on to monitor techniques that enable us to identify performance bottlenecks and optimize performance metrics. Next, we look at techniques that help to us achieve high performance: memory optimization, concurrency, multi threading, scaling, and caching. We also look at fault tolerance solutions and the importance of logging. We'll also cover best practices of Kotlin programming that will help you to improve the quality of your code base.

By the end of the book, you will have gained some insight into various techniques and solutions that will help to create high-performance applications in the Kotlin environment

What you will learn

  • Understand the importance of high performance
  • Learn performance metrics
  • Learn popular design patterns currently being used in Kotlin
  • Understand how to apply modern Kotlin features to data processing
  • Learn how to use profling tools
  • Discover how to read bytecode
  • Learn to perform memory optimizations
  • Uncover approaches to the multithreading environment

Who this book is for

This book is for Kotlin developers who would like to build reliable and high-performance applications. Prior Kotlin programming knowledge is assumed.

Igor Kucherenko is an Android developer at Techery, a software development company that uses Kotlin as the main language for Android development. Currently, he lives in Ukraine, where he is a speaker in the Kotlin Dnipro Community, which promotes Kotlin and shares knowledge with audiences at meetups. You can find his articles about Kotlin and Android development on Medium and a blog for Yalantis, where he worked previously.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 259

Veröffentlichungsjahr: 2018

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Mastering High Performance with Kotlin

 

 

 

 

 

 

 

 

 

 

Overcome performance difficulties in Kotlin with a range of exciting techniques and solutions

 

 

 

 

 

 

 

 

 

 

Igor Kucherenko

 

 

 

 

 

 

 

 

 

 

 

BIRMINGHAM - MUMBAI

Mastering High Performance with Kotlin

Copyright © 2018 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Commissioning Editor: Richa TripathiAcquisition Editor: Sandeep MishraContent Development Editor: Zeeyan PinheiroTechnical Editor: Ruvika RaoCopy Editor: Safis EditingProject Coordinator: Vaidehi SawantProofreader: Safis EditingIndexer: Tejal Daruwale SoniGraphics: Jason MonteiroProduction Coordinator: Shraddha Falebhai

First published: June 2018

Production reference: 1120618

Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.

ISBN 978-1-78899-664-8

www.packtpub.com

mapt.io

Mapt is an online digital library that gives you full access to over 5,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

Why subscribe?

Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

Improve your learning with Skill Plans built especially for you

Get a free eBook or video every month

Mapt is fully searchable

Copy and paste, print, and bookmark content

PacktPub.com

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.

Contributors

About the author

Igor Kucherenko is an Android developer at Techery, a software development company that uses Kotlin as the main language for Android development. Currently, he lives in Ukraine, where he is a speaker in the Kotlin Dnipro Community, which promotes Kotlin and shares knowledge with audiences at meetups. You can find his articles about Kotlin and Android development on Medium and a blog for Yalantis, where he worked previously.

I'd like to thank my colleague for sharing knowledge, and Packt Publishing for the opportunity to write this book; Russell Snyder for his help with many details; my wife for her patience while I was writing this book.

About the reviewer

Ganesh Samarthyam is a co-founder of CodeOps Technologies, a software technology, consultancy, and training company based in Bangalore. He has 16 years of experience in the IT industry, and his latest book, Refactoring for Software Design Smells: Managing Technical Debt by Morgan Kaufmann/Elsevier, has been translated to languages such as Korean and Chinese. Ganesh loves exploring anything and everything about technology in his free time.

 

 

 

 

Packt is searching for authors like you

If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.

Table of Contents

Title Page

Copyright and Credits

Mastering High Performance with Kotlin

Packt Upsell

Why subscribe?

PacktPub.com

Contributors

About the author

About the reviewer

Packt is searching for authors like you

Preface

Who this book is for

What this book covers

To get the most out of this book

Download the example code files

Download the color images

Conventions used

Get in touch

Reviews

Identifying Performance Bottlenecks

Reasons for performance issues

Memory management

Garbage collection

Working principles of the garbage collector

Impacts of garbage collection

Heap fragmentation

Finalization

Resource leaks

Memory leaks

String pool

Memory model

The problem of concurrency and parallelism

Java Memory Model (JMM)

Synchronization

Slow rendering

Device refresh rate

Frame rate

Summary

Identifying Indicators of Performance Issues

Benchmarking

Microbenchmarks

Java Microbenchmark Harness (JMH)

Benchmark modes

Benchmark time units

Benchmark state

State scope

Fixture methods

Levels of fixture methods

Writing good benchmarks

The pitfalls of loops

Dead Code Elimination

Using black holes

Constant folding

Kotlin benchmarks

IDEA JMH plugin

General performance metrics

Types of performance testing 

Performance testing process

Overview of performance testing tools

JMeter

Fabric

Summary

Learning How to Use Profiling Tools

Memory profiling

Memory Viewer

HProf

Eclipse Memory Analyzer Tool

Histogram

Shallow and Retained Heap

Dominator Tree

Top Consumers

Analyzing class loaders

Android Studio Memory Profiler

Memory categories

Recording memory allocation

Threads profiling

Threads viewer

Frames pane

The Threads pane

Thread profiling in the MAT

Threads Overview

Thread Details

CPU Profiler in Android Studio

Call Chart and Flame Chart tabs

Top Down and Bottom Up tabs

Summary

Functional Approach

Functional programming

Declarative versus imperative

Pure functions

First-class functions

Higher-order functions

Function composition

Lambdas

Closures

Typeclasses

Arrow library

Functor

Monad

Immutability

Inspecting functional features

Inline versus non-inline

Capturing and non-capturing lambdas

Summary

Enhancing the Performance of Collections

Data structures

Arrays

Linked lists

Stacks

Queues

Sets

Maps

Hash tables

Binary search trees

Tries

Binary heaps

Graphs

Time complexity

Calculating time complexity

Time complexity of lists

Time complexity of sets

Time complexity of queues

Time complexity of maps

Collections in Kotlin

Sequences in Kotlin

Summary

Optimizing Access to Properties

Fields and properties

Backing fields

Backing properties

@JvmField annotation

Properties inspection

Compile-time constants

Inner classes

lateinit

Companion objects

Summary

Preventing Unnecessary Overhead Using Delegates

Types of delegation

Explicit delegation

Implicit delegation

Class delegation

Delegated properties

Lazy

Creating a delegated property

Inspecting the lazy function

Observable delegates

Summary

Ranges and Identifying Performance Issues

Ranges in Kotlin

Utility functions

Range benchmarking

Summary

Multithreading and Reactive Programming

Concurrency and parallelism

Reactive programming

Coroutines

Setting up a project

The launch function

The async function

The buildSequence function

The suspendingSequence function

The produce function

Coroutines with reactive programming

Summary

Best Practices

The disposable pattern

Immutability

Data classes

The String pool

StringBuilder

Functional programming

Declarative versus imperative

Pure functions

First-class functions

Higher-order functions

Inline functions

Function composition

Capturing lambdas

Collections

Properties

Backing properties

@JvmField annotation

Top-level members

Compile-time constants

The lateinit modifier

Delegation

Singleton delegate object

The lazy funcation with unsafe thread mode

Delegate object with generic

Ranges

Concurrency and parallelism

Summary

Other Books You May Enjoy

Leave a review - let other readers know what you think

Preface

Kotlin is a statically typed programming language that is designed to interoperate with Java code. Since the kotlinc compiler generates the same bytecode as javac, migrating to a new code base doesn't require a lot of effort. Kotlin is a modern language that contains many features from different paradigms that allow you to write concise and safe code. In light of all these points, Kotlin is growing in popularity and increasing in the number of developers who use it.

The book starts by analyzing different performance metrics and debugging tools that help us to identify performance bottlenecks. It's important to understand how Kotlin and its modern features work under the hood; that's why we will inspect a lot of bytecode in the book. We will learn how to benchmark our code and avoid different types of resource leaks. The book ends with best practices that will help you to sum up all things that you've learned.

Who this book is for

This book is for developers who would like to gain deeper understanding of how Kotlin works under the hood. The material doesn't depend on a certain platform of framework, but it focuses on Java Virtual Machine. This book doesn't cover topics about Kotlin to JavaScript feature and Kotlin/Native. It's a good choice for client-side developers because it contains examples with user interfaces and multithreaded environments. 

What this book covers

Chapter 1, Identifying Performance Bottlenecks, covers the reasons for performance issues and how they can impact the user experience and performance of the whole software product. It is an overview of the issues this book aims to resolve, and prepares the reader for the details of these issues in subsequent chapters.

Chapter 2, Identifying Performance Issues, presents performance as one of the leading challenges you will encounter while building any software application. Response speed is one of the critical criteria to analyze performance issues, and it closely correlates with the success of that software in the market. We can use different parameters to identify problems depending on the architecture and what technology is used in the implementation.

Chapter 3, Learn How to Use Profiling Tools, introduces various techniques of troubleshooting application code. We will look at the different profiling tools that measure space of memory, the frequency and duration of function calls, and which software is included in the IntelliJ IDEA platform for this purpose.

Chapter 4, Functional Approach, introduces the functional features of Kotlin. A significant one is the lambda expression, since it allows us to write functions in a much simpler way.We will figure out the bytecode of lambda and the equivalent code in Java to get an understanding of how to use it with minimal overhead.

Chapter 5, Enhancing the Performance of Collections, covers topics related to collections, including their innerworkings and performance enhancements. We will look at the Iterator pattern and its pros and cons.

Chapter 6, Optimizing Access to Properties, covers topics that relate to fields and properties. Virtual method calls are expensive, much more so than instance field lookups. In this chapter, readers will learn how to write the code in such a way as to avoid generation getters and setters in bytecode.

Chapter 7, Preventing Unnecessary Overhead using Delegated Properties, introduces a powerful mechanism that allows us to reduce the amount of code and separate the logic of an application. But we should remember that we have the same bytecode as Java under the hood, and it can be the reason for creating extra objects.

Chapter 8, Ranges and Identifying Performance Issues, introduces ranges in Kotlin that service to represent a sequence of values. This chapter covers some performance issues that can occur while you are working on this feature.

Chapter 9, Multithreading and Reactive Programming, covers basic coroutine-related topics, including concurrency and parallelism. It shows how to invoke long-term operations sequentially in order to avoid Callback hell.

Chapter 10, Best Practices, summarizes the book and contains general tips on how to avoid performance issues in an Android application. Readers will be given general rules that will help them avoid performance issues.

To get the most out of this book

To run examples from this book, you will need a computer running Windows, Linux, or Mac OS. You also need IntelliJ IDEA (Ultimate edition version is preferable) and Android Studio. You need basic knowledge of GitHub and Git to clone a project with examples.

Since Kotlin is an official language for Android development, Android Studio supports this language out of the box. For IntelliJ IDEA you need to install a plugin that is available for download from https://plugins.jetbrains.com/plugin/6954-kotlin.

Download the example code files

You can download the example code files for this book from your account at www.packtpub.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.

You can download the code files by following these steps:

Log in or register at www.packtpub.com.Select the SUPPORT tab.Click on Code Downloads & Errata.Enter the name of the book in the Search box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

WinRAR/7-Zip for WindowsZipeg/iZip/UnRarX for Mac7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on GitHub athttps://github.com/PacktPublishing/Mastering-High-Performance-with-Kotlin. In case there's an update to the code, it will be updated on the existing GitHub repository.

We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

Download the color images

We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: https://www.packtpub.com/sites/default/files/downloads/MasteringHighPerformancewithKotlin_ColorImages.pdf.

Conventions used

There are a number of text conventions used throughout this book.

CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "To override finalize(), all you need to do is simply declare it without using the overridekeyword (and it can't be private)."

A block of code is set as follows:

class C { protected fun finalize() { // finalization logic }}

Any command-line input or output is written as follows:

computation

CacheThread 8

NetworkThread 8

Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "The IntelliJ IDEA has a special function for this. Go to the CalculatorMachine class and open the Generate pane."

Warnings or important notes appear like this.
Tips and tricks appear like this.

Get in touch

Feedback from our readers is always welcome.

General feedback: Email [email protected] and mention the book title in the subject of your message. If you have questions about any aspect of this book, please email us at [email protected].

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Reviews

Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

For more information about Packt, please visit packtpub.com.

Identifying Performance Bottlenecks

How well does it work? How fast is it? These questions mean essentially the same thing if we're talking about software. Although the question about saving technical resources isn't as relevant as it was in the early years of the computer industry, developers still should be careful about the efficiency of systems. Even though efficiency starts with hardware, modern computers have such large instruction sets that it's possible to use them in any manner.

Engineers spend a lot of time and effort to avoid a drain on the Central Processing Unit (CPU), to save battery life, or to make an animation of the user interface much smoother. So the question about performance is relevant nowadays, and software engineers should be careful with system resources.

Before we begin, let's review the topics we will be looking at:

Reasons for performance issues

Memory model

Slow rendering

Reasons for performance issues

Performance is a complicated term that can include response time, the speed of data transmission, availability, and utilization of computer resources. First of all, we should remember that we develop software for users, and so we should concentrate on factors that affect their experience.

Different issues can influence overall system performance differently. In one case, we can have a slow rendering speed; in another case, the response time can be slow. Poor performance decreases productivity, damages the loyalty of customers, and costs the software industry millions of dollars annually. So it would be better to identify bottlenecks before they begin to have a negative influence on the user experience.

Today's customers have applications with legacy code that require upgrading throughputs and response time. Java is one of the most popular languages in the world. A lot of server-side mobile applications and software for SIM cards have been written in Java. But Java isn't a modern programming language. This is the main reason for the appearance of Kotlin. It allows you to write simpler and more reliable code. The fact that Kotlin can compile to the same bytecode as Java is why applications written in these different languages can have the same performance. That's why the question about migrating from Java to Kotlin is relevant nowadays, and developers should be prepared for it. We're going to uncover the main reasons for performance issues that relate to all applications that are based on the Java Virtual Machine (JVM) and consequently to Kotlin.

Memory management

Memory is one of the essential resources of a computer, and it's essential to manage it properly. Failure to do so can lead to slow performance and bugs such as arithmetic overflow, memory leaks, segmentation faults, and buffer overflows.

The primary purpose of a memory management system is to provide the ability to dynamically allocate the requested size of memory to programs and to release it for reuse when no longer needed. These systems perform management on two levels:

Operating-system level

Application level

We'll concentrate on the application level because it's the responsibility of an application software developer. The operating-system level is managed with an operating system.

There are two types of application-level management systems:

Automatic memory management

Manual memory management

Manual memory management assumes that the programmer uses manual instructions to release unused garbage. It's relevant to languages (still in wide use today) such as C and C++. The JVM has automatic memory management that involves the garbage collection.

Garbage collection

Garbage collection is a strategy for automatically detecting memory allocated to objects that are no longer usable in a program and returning that allocated memory to the pool of free memory locations. All memory management techniques, including garbage collection, take a significant proportion of a program's total processing time and, as a result, can greatly influence performance. With modern, optimized garbage collection algorithms, memory can be released faster than with manual memory management. But depending on the application, the opposite can also be true, and many developers prefer to deallocate memory themselves. One of the biggest advantages that manual memory management has is the ability to reclaim resources before an object is destroyed. This process is referred to as finalization, and we'll touch on it further because it can also be a performance issue.

Memory management is an essential process that's applied to the computer memory. Since the JVM uses automatic memory management with the garbage collection strategy we should know what it is and how it works.

Working principles of the garbage collector

The garbage collection strategy assumes that the developer doesn't explicitly release memory. Instead, the garbage collector (GC) finds objects that aren't being used anymore and destroys them. As GC sees it, there are two types of objects—reachable and unreachable. This principle is based on a set of root objects that are always reachable. An object is a root if it satisfies one of the following criteria:

Local variables

: They are stored in the stack of a thread

.

 When a method, constructor, or initialization block is entered, local variables are created and become unreachable and available for the GC once they exit the scope of the method, constructor, or initialization block.

Active threads

: These are objects that hold other ones from the GC's point of view. So all these objects are a reference tree that will not be destroyed until the thread is terminated.

Static variables

: They are referenced by instances of the 

Class

type where they're defined. The metadata of classes is kept in the Metaspace section of memory. This makes static variables de facto roots. When a

classLoader

loads and instantiates a new object of the 

Class

 type, static variables are created and can be destroyed during major garbage collection.

Java native interface references

: They are references to objects that are held in native code. These objects aren't available to the GC because they can be used outside the JVM environment. These references require manual management in native code. That's why they often become the reason for memory leaks and performance issues.

The following diagram illustrates a simplified schematic of references trees:

An object is reachable if it's a leaf from a reference tree that's reachable from the root object. If an object is unreachable, then it's available for the GC. Since the GC starts collecting at unpredictable times, it's hard to tell when the memory space will be deallocated.

To perform garbage collection, the JVM needs to stop the world. This means that the JVM stops all threads except those that are needed for garbage collection. This procedure guarantees that new objects aren't created and that old objects don't suddenly become unreachable while the GC is working. Modern GC implementations do as much work as possible in the background thread. For instance, the mark and sweep algorithm marks reachable objects while the application continues to run, and parallel collectors split a space of the heap into small sections, and a separate thread works on each one.

The memory space is divided into several primary generations—young, old, and permanent. The permanent generation contains static members and the metadata about classes and methods. Newly created objects belong to the young generation, and once there's no reference to any of them, it becomes available for minor garbage collection. After this, the surviving objects are moved to the old generation, and become available only for major garbage collection.

Impacts of garbage collection

Depends on algorithm, the performance of garbage collection can depend on the number of objects or the size of the heap. GC needs time to detect reachable and unreachable objects. During this step, automatic memory management might lose out to manual memory management because a developer may have already known which objects should be destroyed. And after this stop the world—also known as the GC pause—is invoked, the GC suspends execution of all threads to ensure the integrity of reference trees.

Heap fragmentation

When the JVM starts, it allocates heap memory from the operating system and then manages that memory. Whenever an application creates a new object, the JVM automatically allocates a block of memory with a size that's big enough to fit the new object on the heap. After sweeping, in most cases, memory becomes fragmented. Memory fragmentation leads to two problems:

Allocation operations become more time consuming, because it's hard to find the next free block of sufficient size

The unused space between blocks can become so great that the JVM won't be able to create a new object

The following diagram illustrates a fragmented memory heap:

To avoid these problems after each GC cycle, the JVM executes a compaction step. Compacting moves all reachable objects to one end of the heap and, in this way, closes all holes. The heap after compacting looks as follows:

These diagrams show how blocks are located before and after compacting. The drawback is that an application must also be suspended during this process.

Finalization

Finalization is a process of releasing resources. It's executed with a finalizer method that's invoked after an object becomes unreachable, but before its memory is deallocated. Finalization is a non-deterministic process because it's not known when garbage collection will occur, and it might never happen.This is in contrast to a destructor, which is a method called for finalization in languages with manual memory management.

The following diagram illustrates the simplified life cycle of an object:

A destructor, in most languages, is the language-level term that means a method defined in a class by a programmer. A finalizer is an implementation-level term that means a method called by a system during object creation or destruction. Finalizers are needed to perform object-specific operations, cleaning or releasing resources that were used with an object. That's why they're most frequently instance methods.

Finalizers have several drawbacks:

It may never be called promptly, so a software engineer cannot rely on it to do something important, such as

persisting

 a state or

releasing 

scarce resources.

The invoking order of finalizers isn't specified.

Garbage collection, and consequently the finalizer, runs when memory resources are terminated but not when it's time to release other scarce resources. So, it's not a good idea to use it to release limited resources.

If too much work is performed in one finalizer, another one may start with a delay. And this may increase the total time of the garbage collection pause.

A finalizer may cause synchronization issues as well because it can use shared variables.

A finalizer in a superclass can also slow down garbage collection in a subclass because it can refer to the same fields.

To implement the finalizer in Java, a developer has to override the finalize() method of the Object class. The Object class has an empty implementation of the following method: 

protected void finalize() throws Throwable { }

This method has good documentation with two interesting moments. The first is that the Java programming language doesn't guarantee which thread will invoke the finalize() method for any given object. It's guaranteed, however, that the thread that invokes the finalize() method will not be holding any user-visible synchronization locks when finalize() is invoked. If an uncaught exception is thrown by the finalize() method, the exception is ignored, and finalization of that object terminates. And the second interesting catch is that any exceptions that are thrown by the finalize() method cause the finalization of this object to be halted, but they are otherwise ignored.

A sample of overriding can be found, for instance, in the source code of the FileInputStream class:

@Override protected void finalize() throws IOException { try { if (guard != null) { guard.warnIfOpen(); } close(); } finally { try { super.finalize(); } catch (Throwable t) { // for consistency with the RI, we must override Object.finalize() to // remove the ’throws Throwable’ clause. throw new AssertionError(t); } }}

This implementation ensures that all resources for this stream are released when it's about to be garbage collected.

But in Kotlin, the root of the class hierarchy is Any, which does not have a finalize() method:

public open class Any { public open operator fun equals(other: Any?): Boolean public open fun hashCode(): Int public open fun toString(): String}

But according to the Kotlin documentation: https://kotlinlang.org/docs/reference/java-interop.html#finalize, to override finalize(), all you need to do is simply declare it without using theoverridekeyword (and it can't be private):

class C { protected fun finalize() { // finalization logic }}

If you read to avoid finalizers and cleaners item of the effective Java book, you know that using finalizers to release resources is a common anti-pattern. To acquire resources in the constructor or initialization block and release it in the finalizer isn't a good approach. It's better to acquire the resources only when needed and release them once they're no longer needed. In other cases, using the finalize() method to release resources can cause resource and memory leaks.