Skip to content

Commit c070a00

Browse files
Add operationalisation section to README, guidance on jemalloc (#164)
Co-authored-by: James Bench <[email protected]>
1 parent d68fa4f commit c070a00

File tree

10 files changed

+147
-18
lines changed

10 files changed

+147
-18
lines changed

.github/workflows/checks.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ jobs:
8787
.\gradlew.bat sample:clean sample:shadowJar
8888
java -jar sample/build/libs/sample-all.jar
8989
90-
docker-checks:
90+
docker-checks-matrix:
9191
runs-on: ubuntu-24.04
92+
strategy:
93+
matrix:
94+
variant: ["debian-12", "ubuntu-2404", "ubuntu-2404-jemalloc"]
9295
steps:
9396
- name: Checkout
9497
uses: actions/checkout@v4
@@ -105,4 +108,11 @@ jobs:
105108
gradle-version: 8.14
106109

107110
- name: Run checks
108-
run: ./run_docker_tests.sh
111+
run: ./run_single_docker_tests.sh ${{ matrix.variant }}
112+
113+
docker-checks:
114+
runs-on: ubuntu-24.04
115+
name: Verify all Docker checks passed
116+
needs: [docker-checks-matrix]
117+
steps:
118+
- run: exit 0

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ library is already used by some larger businesses (which is cool!), and I care a
1515
Please discuss any bigger changes with me **before** submitting a Pull Request - I can help you refine your idea better
1616
that way, and I don't want to waste anybody's time: [Discussions](https://github.com/lopcode/vips-ffm/discussions).
1717

18+
As part of a pull request I will probably edit commits on the branch, and will squash them down, but will be careful to
19+
retain your contributor metadata so you're named appropriately as a contributor on GitHub. GitHub Actions workflows to
20+
run the project's tests require approval - I'll do this when I'm reviewing the PR.
21+
1822
I haven't currently defined a code of conduct for this project specifically, but please refer to the CoC [in libvips](https://github.com/libvips/libvips/blob/master/CODE_OF_CONDUCT.md)
1923
for guidance on expected behaviour.
2024

README.md

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Fast, safe, complete [libvips](https://github.com/libvips/libvips) bindings for
44

55
Supports a vast range of image formats, including HEIC, JXL, WebP, PNG, JPEG, and more. Pronounced "vips (like zips)
66
eff-eff-emm". The project is relatively new, but aims to be production ready. Tested on macOS 14, Windows 11, and Linux
7-
(Ubuntu 24.04, Debian 12.1). Should work on any architecture you can use libvips and Java on (arm64/amd64/etc).
7+
(Ubuntu 24.04, Debian 12.1, with and without jemalloc). Should work on any architecture you can use libvips and
8+
Java on (arm64/amd64/etc).
89

910
Uses the "Foreign Function & Memory API" ([JEP 454](https://openjdk.org/jeps/454)), and the "Class-File API" ([JEP 457](https://openjdk.org/jeps/457)) released in JDK 22.
1011
Built in such a way that it's usually the fastest image processing library available for Java.
@@ -57,7 +58,7 @@ import app.photofox.vipsffm.enums.VipsAccess
5758

5859
// Call once to initialise libvips when your program starts, from any thread
5960
// Note that by default this blocks untrusted operations (like loading PDFs)
60-
// Use `Vips.init(true, ...)` to permit untrusted operations
61+
// See the "Allowing untrusted operations" section below to read about permitting untrusted operations
6162
Vips.init()
6263

6364
// Use `Vips.run` to wrap your usage of the API, and get an arena with an appropriate lifetime to use
@@ -193,6 +194,60 @@ like Android where it's hard to set the system library path), you can do so usin
193194
* glib: `vipsffm.libpath.glib.override`
194195
* gobject: `vipsffm.libpath.gobject.override`
195196

197+
## Operationalisation
198+
199+
libvips maintain [a checklist](https://www.libvips.org/API/8.17/developer-checklist.html) of things to be aware of when
200+
using the library. Of particular note for vips-ffm is memory usage - especially if the library is used for long-running
201+
application (like a server).
202+
203+
### Operation cache
204+
205+
At the time of writing, libvips maintains a cache of the 100 most recent operations ([see docs](https://www.libvips.org/API/8.17/how-it-works.html#operation-cache)).
206+
If running an image proxy, or something that processes lots of different images, you won't see any benefit, and can
207+
disable it:
208+
209+
```java
210+
Vips.init();
211+
Vips.disableOperationCache();
212+
```
213+
214+
### Memory allocation
215+
216+
On glibc-based Linux systems (e.g. Debian, Red Hat), the default memory allocator performs poorly for long-running,
217+
multithreaded processes with frequent small allocations. Using an alternative allocator like jemalloc can reduce the
218+
off-heap footprint of the JVM when using libvips.
219+
220+
Note that the jemalloc project is going through [some turbulence](https://jasone.github.io/2025/06/12/jemalloc-postmortem/)
221+
at the moment. Facebook have [forked it](https://github.com/facebook/jemalloc), though its maintenance status is
222+
currently unknown.
223+
224+
An example of using jemalloc on Ubuntu:
225+
1. Install jemalloc
226+
```sh
227+
apt install libjemalloc2
228+
```
229+
2. Set the `LD_PRELOAD` environment variable before running your application.
230+
```sh
231+
ln -sT "$(readlink -e /usr/lib/*/libjemalloc.so.2)" /usr/local/lib/libjemalloc.so # symlink jemalloc to a standard location
232+
export LD_PRELOAD=/usr/local/lib/libjemalloc.so
233+
java -jar ...
234+
```
235+
236+
### Allowing untrusted operations
237+
238+
By default, vips-ffm sets the "block untrusted operations" flag in libvips, in an attempt to be "secure by default".
239+
This includes blocking things like the imagemagick and PDF loaders. If you get an error relating to "operation is
240+
blocked", then the operation you're trying to use is marked as untrusted in libvips.
241+
242+
If you need to work with operations and formats that are marked as "untrusted" in libvips, you can permit them
243+
explicitly:
244+
```java
245+
Vips.allowUntrustedOperations();
246+
```
247+
248+
See the [libvips docs](https://www.libvips.org/API/8.17/func.block_untrusted_set.html) for guidance on figuring out what
249+
loaders and operations are marked as trusted or untrusted.
250+
196251
## Project goals
197252

198253
Ideas and suggestions are welcome, but please make sure they fit in to these goals, or you have a good argument about
@@ -224,4 +279,4 @@ Thank you for being enthusiastic about the project!
224279
* And only after a GitHub Release is made
225280
* Run `./publish_release_to_maven_central.sh <version matching github release version, including v prefix>`
226281

227-
[1]: https://docs.oracle.com/en/java/javase/23/core/memory-segments-and-arenas.html
282+
[1]: https://docs.oracle.com/en/java/javase/23/core/memory-segments-and-arenas.html

core/src/main/java/app/photofox/vipsffm/Vips.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ public static void init(boolean allowUntrusted, boolean detectLeaks) {
1717
VipsHelper.leak_set(detectLeaks);
1818
}
1919

20+
/// Provides a scoped arena to provide a memory boundary for running libvips operations
21+
///
22+
/// After the scope has ended, any memory allocated whilst using libvips within it will be freed
2023
public static void run(VipsRunnable runnable) {
2124
try (var arena = Arena.ofConfined()) {
2225
runnable.run(arena);
@@ -26,4 +29,22 @@ public static void run(VipsRunnable runnable) {
2629
public static void shutdown() {
2730
VipsHelper.shutdown();
2831
}
32+
33+
/// Permits untrusted operations, such as loading PDFs
34+
///
35+
/// vips-ffm blocks these by default - see the [libvips docs](https://www.libvips.org/API/8.17/func.block_untrusted_set.html)
36+
/// for guidance
37+
public static void allowUntrustedOperations() {
38+
VipsHelper.block_untrusted_set(false);
39+
}
40+
41+
/// Disables the libvips operations cache
42+
///
43+
/// At the time of writing libvips caches 100 operations by default, which might not be useful in long-running
44+
/// applications (like servers).
45+
///
46+
/// See also: [libvips docs](https://www.libvips.org/API/8.17/how-it-works.html#operation-cache)
47+
public static void disableOperationCache() {
48+
VipsHelper.cache_set_max(0);
49+
}
2950
}

docker_tests/debian-12/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ENV PATH="${JAVA_HOME}/bin:${PATH}"
66
COPY sample /opt/sample
77
COPY run_samples.sh /opt/run_samples.sh
88

9-
RUN apt update && apt install libvips-dev -y
9+
RUN apt update && apt install --no-install-recommends --yes libvips-dev libvips-tools libjemalloc2
1010
RUN vips --version
1111

1212
WORKDIR /opt
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM ubuntu:24.04
2+
ENV JAVA_HOME=/opt/java/openjdk
3+
COPY --from=eclipse-temurin:23 $JAVA_HOME $JAVA_HOME
4+
ENV PATH="${JAVA_HOME}/bin:${PATH}"
5+
6+
COPY sample /opt/sample
7+
COPY run_samples.sh /opt/run_samples.sh
8+
9+
RUN apt update && apt install --no-install-recommends --yes libvips-dev libvips-tools libjemalloc2
10+
RUN ln -sT "$(readlink -e /usr/lib/*/libjemalloc.so.2)" /usr/local/lib/libjemalloc.so
11+
12+
RUN vips --version
13+
14+
WORKDIR /opt
15+
CMD ./run_samples.sh

docker_tests/ubuntu-2404/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ENV PATH="${JAVA_HOME}/bin:${PATH}"
66
COPY sample /opt/sample
77
COPY run_samples.sh /opt/run_samples.sh
88

9-
RUN apt update && apt install libvips-dev -y
9+
RUN apt update && apt install --no-install-recommends --yes libvips-dev libvips-tools libjemalloc2
1010
RUN vips --version
1111

1212
WORKDIR /opt

run_docker_tests.sh

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,11 @@
22
set -eou pipefail
33

44
echo "building samples..."
5-
./gradlew sample:clean sample:shadowJar
5+
./gradlew sample:shadowJar
66

7-
echo "running docker tests..."
8-
WORKSPACE_DIR="$PWD"
7+
echo "running all docker tests..."
98

10-
docker_tests=("debian-12" "ubuntu-2404")
9+
docker_tests=("ubuntu-2404-jemalloc" "debian-12" "ubuntu-2404")
1110
for docker_test in "${docker_tests[@]}"; do
12-
echo "testing \"$docker_test\""
13-
pushd "docker_tests/$docker_test"
14-
cp -r "$WORKSPACE_DIR"/sample .
15-
cp "$WORKSPACE_DIR"/run_samples.sh .
16-
docker build -t "vips-ffm-$docker_test-test" .
17-
docker run "vips-ffm-$docker_test-test"
18-
popd
11+
./run_single_docker_tests.sh "$docker_test" || (echo "test failed" && exit 1)
1912
done

run_samples.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
1515
export JAVA_PATH_OPTS="-Dvipsffm.libpath.vips.override=/opt/homebrew/lib/libvips.dylib"
1616
fi
1717

18+
if test -f /usr/local/lib/libjemalloc.so; then
19+
echo "found jemalloc - using it"
20+
export LD_PRELOAD=/usr/local/lib/libjemalloc.so
21+
fi
22+
1823
echo "running samples..."
1924
java $JAVA_PATH_OPTS -jar sample/build/libs/sample-all.jar 2>&1 | tee sample_output.log
2025

run_single_docker_tests.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
set -eou pipefail
3+
4+
if [ -z "$1" ]; then
5+
echo "usage: ./run_single_docker_tests.sh ubuntu-2404"
6+
exit 1
7+
fi
8+
9+
echo "building samples..."
10+
./gradlew sample:shadowJar
11+
12+
echo "verifying docker test variant \"$1\" exists..."
13+
if [ ! -d "docker_tests/$1" ]; then
14+
echo "could not find docker test variant - exiting"
15+
exit 1
16+
fi
17+
18+
echo "running docker tests for variant $1..."
19+
WORKSPACE_DIR="$PWD"
20+
21+
pushd "docker_tests/$1"
22+
cp -r "$WORKSPACE_DIR"/sample .
23+
cp "$WORKSPACE_DIR"/run_samples.sh .
24+
docker build --progress=plain -t "vips-ffm-$1-test" .
25+
docker run "vips-ffm-$1-test"
26+
popd

0 commit comments

Comments
 (0)