logo资料库

Optimizing Java: Practical Techniques for Improving JVM Applicat....pdf

第1页 / 共502页
第2页 / 共502页
第3页 / 共502页
第4页 / 共502页
第5页 / 共502页
第6页 / 共502页
第7页 / 共502页
第8页 / 共502页
资料共502页,剩余部分请下载后查看
Foreword
Preface
Conventions Used in This Book
Using Code Examples
O’Reilly Safari
How to Contact Us
Acknowledgments
1. Optimization and Performance Defined
Java Performance—The Wrong Way
Java Performance Overview
Performance as an Experimental Science
A Taxonomy for Performance
Throughput
Latency
Capacity
Utilization
Efficiency
Scalability
Degradation
Connections Between the Observables
Reading Performance Graphs
Summary
2. Overview of the JVM
Interpreting and Classloading
Executing Bytecode
Introducing HotSpot
Introducing Just-in-Time Compilation
JVM Memory Management
Threading and the Java Memory Model
Meet the JVMs
A Note on Licenses
Monitoring and Tooling for the JVM
VisualVM
Summary
3. Hardware and Operating Systems
Introduction to Modern Hardware
Memory
Memory Caches
Modern Processor Features
Translation Lookaside Buffer
Branch Prediction and Speculative Execution
Hardware Memory Models
Operating Systems
The Scheduler
A Question of Time
Context Switches
A Simple System Model
Basic Detection Strategies
Utilizing the CPU
Garbage Collection
I/O
Mechanical Sympathy
Virtualization
The JVM and the Operating System
Summary
4. Performance Testing Patterns and Antipatterns
Types of Performance Test
Latency Test
Throughput Test
Load Test
Stress Test
Endurance Test
Capacity Planning Test
Degradation Test
Best Practices Primer
Top-Down Performance
Creating a Test Environment
Identifying Performance Requirements
Java-Specific Issues
Performance Testing as Part of the SDLC
Introducing Performance Antipatterns
Boredom
Résumé Padding
Peer Pressure
Lack of Understanding
Misunderstood/Nonexistent Problem
Performance Antipatterns Catalogue
Distracted by Shiny
Distracted by Simple
Performance Tuning Wizard
Tuning by Folklore
The Blame Donkey
Missing the Bigger Picture
UAT Is My Desktop
Production-Like Data Is Hard
Cognitive Biases and Performance Testing
Reductionist Thinking
Confirmation Bias
Fog of War (Action Bias)
Risk Bias
Ellsberg’s Paradox
Summary
5. Microbenchmarking and Statistics
Introduction to Measuring Java Performance
Introduction to JMH
Don’t Microbenchmark If You Can Help It (A True Story)
Heuristics for When to Microbenchmark
The JMH Framework
Executing Benchmarks
Statistics for JVM Performance
Types of Error
Non-Normal Statistics
Interpretation of Statistics
Summary
6. Understanding Garbage Collection
Introducing Mark and Sweep
Garbage Collection Glossary
Introducing the HotSpot Runtime
Representing Objects at Runtime
GC Roots and Arenas
Allocation and Lifetime
Weak Generational Hypothesis
Garbage Collection in HotSpot
Thread-Local Allocation
Hemispheric Collection
The Parallel Collectors
Young Parallel Collections
Old Parallel Collections
Limitations of Parallel Collectors
The Role of Allocation
Summary
7. Advanced Garbage Collection
Tradeoffs and Pluggable Collectors
Concurrent GC Theory
JVM Safepoints
Tri-Color Marking
CMS
How CMS Works
Basic JVM Flags for CMS
G1
G1 Heap Layout and Regions
G1 Algorithm Design
G1 Phases
Basic JVM Flags for G1
Shenandoah
Concurrent Compaction
Obtaining Shenandoah
C4 (Azul Zing)
The Loaded Value Barrier
Balanced (IBM J9)
J9 Object Headers
Large Arrays in Balanced
NUMA and Balanced
Legacy HotSpot Collectors
Serial and SerialOld
Incremental CMS (iCMS)
Deprecated and Removed GC Combinations
Epsilon
Summary
8. GC Logging, Monitoring, Tuning, and Tools
Introduction to GC Logging
Switching On GC Logging
GC Logs Versus JMX
Drawbacks of JMX
Benefits of GC Log Data
Log Parsing Tools
Censum
GCViewer
Different Visualizations of the Same Data
Basic GC Tuning
Understanding Allocation
Understanding Pause Time
Collector Threads and GC Roots
Tuning Parallel GC
Tuning CMS
Concurrent Mode Failures Due to Fragmentation
Tuning G1
jHiccup
Summary
9. Code Execution on the JVM
Overview of Bytecode Interpretation
Introduction to JVM Bytecode
Simple Interpreters
HotSpot-Specific Details
AOT and JIT Compilation
AOT Compilation
JIT Compilation
Comparing AOT and JIT Compilation
HotSpot JIT Basics
Klass Words, Vtables, and Pointer Swizzling
Logging JIT Compilation
Compilers Within HotSpot
Tiered Compilation in HotSpot
The Code Cache
Fragmentation
Simple JIT Tuning
Summary
10. Understanding JIT Compilation
Introducing JITWatch
Basic JITWatch Views
Debug JVMs and hsdis
Introducing JIT Compilation
Inlining
Inlining Limits
Tuning the Inlining Subsystem
Loop Unrolling
Loop Unrolling Summary
Escape Analysis
Eliminating Heap Allocations
Locks and Escape Analysis
Limitations of Escape Analysis
Monomorphic Dispatch
Intrinsics
On-Stack Replacement
Safepoints Revisited
Core Library Methods
Upper Method Size Limit for Inlining
Upper Method Size Limit for Compilation
Summary
11. Java Language Performance Techniques
Optimizing Collections
Optimization Considerations for Lists
ArrayList
LinkedList
ArrayList versus LinkedList
Optimization Considerations for Maps
HashMap
TreeMap
Lack of MultiMap
Optimization Considerations for Sets
Domain Objects
Avoid Finalization
War Story: Forgetting to Clean Up
Why Not Use Finalization to Solve the Problem?
try-with-resources
Method Handles
Summary
12. Concurrent Performance Techniques
Introduction to Parallelism
Fundamental Java Concurrency
Understanding the JMM
Building Concurrency Libraries
Unsafe
Atomics and CAS
Locks and Spinlocks
Summary of Concurrent Libraries
Locks in java.util.concurrent
Read/Write Locks
Semaphores
Concurrent Collections
Latches and Barriers
Executors and Task Abstraction
Introducing Asynchronous Execution
Selecting an ExecutorService
Fork/Join
Modern Java Concurrency
Streams and Parallel Streams
Lock-Free Techniques
Actor-Based Techniques
Summary
13. Profiling
Introduction to Profiling
Sampling and Safepointing Bias
Execution Profiling Tools for Developers
VisualVM Profiler
JProfiler
YourKit
Flight Recorder and Mission Control
Operational Tools
Modern Profilers
Allocation Profiling
Heap Dump Analysis
hprof
Summary
14. High-Performance Logging and Messaging
Logging
Logging Microbenchmarks
Designing a Lower-Impact Logger
Low Latency Using Real Logic Libraries
Agrona
Simple Binary Encoding
Aeron
The Design of Aeron
Summary
15. Java 9 and the Future
Small Performance Enhancements in Java 9
Segmented Code Cache
Compact Strings
New String Concatenation
C2 Compiler Improvements
New Version of G1
Java 10 and Future Versions
New Release Process
Java 10
Unsafe in Java 9 and Beyond
VarHandles in Java 9
Project Valhalla and Value Types
Graal and Truffle
Future Directions in Bytecode
Future Directions in Concurrency
Conclusion
Index
Optimizing Java Practical Techniques for Improving JVM Application Performance Benjamin J. Evans, James Gough, and Chris Newland
Optimizing Java by Benjamin J. Evans, James Gough, and Chris Newland Copyright © 2018 Benjamin J. Evans, James Gough, and Chris Newland. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com/safari). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Editors: Susan Conant and Virginia Wilson Production Editor: Colleen Cole Copyeditor: Rachel Monaghan Proofreader: Rachel Head Indexer: Ellen Troutman-Zaig Interior Designer: David Futato Cover Designer: Randy Comer Illustrator: Anna Evans Technical Reviewers: Julian Templeman, Michael Hsu, Alex Blewitt, Dmitry Vyazelenko May 2018: First Edition Revision History for the First Edition 2018-04-12: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781492025795 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Optimizing Java, the
cover image, and related trade dress are trademarks of O’Reilly Media, Inc. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 978-1-492-02579-5 [LSI]
Dedication This book is dedicated to my wife, Anna, who not only illustrated it beautifully, but also helped edit portions and, crucially, was often the first person I bounced ideas off. —Benjamin J. Evans This book is dedicated to my incredible family Megan, Emily, and Anna. Writing would not have been possible without their help and support. I’d also like to thank my parents, Heather and Paul, for encouraging me to learn and their constant support. I’d also like to thank Benjamin Evans for his guidance and friendship—it’s been a pleasure working together again. —James Gough This book is dedicated to my wife, Reena, who supported and encouraged my efforts and to my sons, Joshua and Hugo, may they grow up with inquisitive minds. —Chris Newland
Foreword How do you define performance? Most developers, when asked about the performance of their application, will assume some measure of speed is requested. Something like transactions per second, or gigabytes of data processed…getting a lot of work done in the shortest amount of time possible. If you’re an application architect, you may measure performance in broader metrics. You may be more concerned about resource utilization than straight-line execution. You might pay more attention to the performance of connections between services than of the services themselves. If you make business decisions for your company, application performance will probably not be measured in time as often as it is measured in dollars. You may argue with developers and architects about resource allocation, weighing the cost of devops against the time it takes to do the company’s work. And regardless of which role you identify with, all these metrics are important. I started out developing Java applications in 1996. I had just moved from my first job writing AppleScript CGIs for the University of Minnesota’s business school to maintaining server-side Perl applications for the web development team. Java was very new then—the first stable version, 1.0.2, was released earlier that year—and I was tasked with finding something useful to build. Back in those days, the best way to get performance out of a Java application was to write it in some other language. This was before Java had a Just-in-Time (JIT) compiler, before parallel and concurrent garbage collectors, and long before the server side would become dominated by Java technology. But many of us wanted to use Java, and we developed all sorts of tricks to make our code run well. We wrote gigantic methods to avoid method dispatch overhead. We pooled and reused objects because garbage collection was slow and disruptive. We used lots of global state and static methods. We wrote truly awful Java code, but it worked…for a little while. In 1999, things started to change. After years struggling to use Java for anything demanding speed, JIT technologies started to reach us. With compilers that could inline methods, the number of method calls became less important than breaking up our giant monoliths into smaller pieces. We gleefully embraced object-oriented design, splitting our methods into tiny chunks and wrapping interfaces around everything. We marveled at how every release of Java would run things just a little bit better, because we were writing good Java code and the JIT compiler loved it. Java soared past other technologies on the server, leading us to build larger and more complex apps with richer abstractions. At the same time, garbage collectors were rapidly improving. Now the overhead of pooling would very frequently overshadow the cost of allocation. Many garbage collectors
offered multithreaded operation, and we started to see low-pause, nearly concurrent GCs that stayed out of our applications’ way. The standard practice moved toward a carefree creating and throwing away of objects with the promise that a sufficiently smart GC would eventually make it all OK. And it worked…for a little while. The problem with technology is that it always invalidates itself. As JIT and GC technologies have improved, the paths to application performance have become tricky to navigate. Even though JVMs can optimize our code and make objects almost free, the demands of applications and users continue to grow. Some of the time, maybe even most of the time, the “good” coding patterns prevail: small methods inline properly, interface and type checks become inexpensive, native code produced by the JIT compiler is compact and efficient. But other times we need to hand- craft our code, dial back abstractions and architecture in deference to the limitations of the compiler and CPU. Some of the time, objects really are free and we can ignore the fact that we’re consuming memory bandwidth and GC cycles. Other times we’re dealing with terabyte-scale (or larger) datasets that put stress on even the best garbage collectors and memory subsystems. The answer to the performance question these days is to know your tools. And frequently, that means knowing not just how Java the language works, but also how JVM libraries, memory, the compiler, GCs, and the hardware your apps run on are interacting. In my work on the JRuby project, I’ve learned an immutable truth about the JVM: there’s no single solution for all performance problems, but for all performance problems there are solutions. The trick is finding those solutions and piecing together the ones that meet your needs best. Now you have a secret weapon in these performance battles: the book you are about to read. Turn the page, friends, and discover the wealth of tools and techniques available to you. Learn how to balance application design with available resources. Learn how to monitor and tune the JVM. Learn how to make use of the latest Java technologies that are more efficient than old libraries and patterns. Learn how to make Java fly. It’s an exciting time to be a Java developer, and there have never been so many opportunities to build efficient and responsive applications on the Java platform. Let’s get started. Charlie Nutter Principal Software Engineer, Red Hat Middleware
Preface Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. in angle brackets Shows text that should be replaced with user-supplied values or by values determined by context. This element signifies a tip or suggestion. This element signifies a general note. TIP NOTE This element indicates a warning or caution. WARNING Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at http://bit.ly/optimizing-java-1e-code-examples.
分享到:
收藏